Apprends à gérer les erreurs d'exécution avec élégance grâce à try/catch/finally, à lancer des erreurs personnalisées et à comprendre les types d'erreurs.
Les erreurs d'exécution sont inévitables en programmation. Sans une gestion appropriée, une seule erreur peut planter tout ton programme et laisser les utilisateurs face à une page cassée.
Toutes les erreurs ne sont pas des bugs dans ton code. Beaucoup proviennent de sources externes que tu ne peux pas contrôler :
null ou undefinedLa gestion des erreurs permet à ton programme de détecter ces problèmes et de réagir avec élégance au lieu de planter :
// Sans gestion des erreurs — ça plante !
const data = JSON.parse('invalid json'); // SyntaxError!
console.log('Ceci ne s'exécute jamais');
// Avec gestion des erreurs — récupération élégante
try {
const data = JSON.parse('invalid json');
} catch (error) {
console.log('Données invalides, utilisation des valeurs par défaut');
const data = {};
}
console.log('Le programme continue de s'exécuter');Une bonne gestion des erreurs améliore l'expérience utilisateur (afficher des messages utiles au lieu d'écrans vides), le débogage (journaliser des informations d'erreur significatives) et la fiabilité (maintenir l'application en marche quand les choses tournent mal).
L'instruction try...catch te permet de tenter du code risqué et de gérer toute erreur qui survient :
try {
// Code qui pourrait lancer une erreur
const result = riskyOperation();
console.log(result);
} catch (error) {
// Code qui s'exécute si une erreur est lancée
console.log('Quelque chose s'est mal passé:', error.message);
}L'objet error dans le bloc catch possède des propriétés utiles :
error.message — une description lisible de l'erreurerror.name — le type d'erreur (ex : 'TypeError', 'SyntaxError')error.stack — une trace de la pile montrant où l'erreur s'est produite (utile pour le débogage)Le bloc finally s'exécute quoi qu'il arrive — que le code ait réussi ou échoué. Il est parfait pour les opérations de nettoyage :
let connection;
try {
connection = openDatabase();
const data = connection.query('SELECT * FROM users');
processData(data);
} catch (error) {
console.log('Erreur de base de données:', error.message);
} finally {
// Toujours fermer la connexion, même s'il y a eu une erreur
if (connection) {
connection.close();
}
}Tu peux aussi imbriquer des blocs try...catch pour une gestion d'erreur plus granulaire :
try {
try {
JSON.parse(input);
} catch (parseError) {
console.log('Analyse échouée, tentative alternative...');
JSON.parse(alternativeInput);
}
} catch (error) {
console.log('Toutes les analyses ont échoué:', error.message);
}Tu peux lancer tes propres erreurs en utilisant l'instruction throw. C'est utile pour appliquer des règles de validation et des contrats de fonction :
function divide(a, b) {
if (b === 0) {
throw new Error('Impossible de diviser par zéro');
}
return a / b;
}
try {
const result = divide(10, 0);
} catch (error) {
console.log(error.message); // 'Impossible de diviser par zéro'
}JavaScript possède plusieurs types d'erreurs intégrés, chacun signalant un type de problème différent :
TypeError — mauvais type : null.property, appel d'une non-fonctionReferenceError — utilisation d'une variable non définieRangeError — valeur hors limites : new Array(-1)SyntaxError — code invalide : JSON.parse('{')URIError — URI mal formée : decodeURI('%')Tu peux lancer des types d'erreur spécifiques pour plus de clarté :
function setAge(age) {
if (typeof age !== 'number') {
throw new TypeError('L'âge doit être un nombre');
}
if (age < 0 || age > 150) {
throw new RangeError('L'âge doit être entre 0 et 150');
}
return age;
}Tu peux aussi créer des classes d'erreur personnalisées pour ton application :
class ValidationError extends Error {
constructor(field, message) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
throw new ValidationError('email', 'Format d'email invalide');Voici des modèles pratiques pour gérer les erreurs efficacement :
1. Valider les entrées tôt (clauses de garde) :
function processUser(user) {
if (!user) throw new Error('Utilisateur requis');
if (!user.email) throw new Error('Email requis');
// ... continuer avec des données valides
}2. Envelopper les opérations risquées dans try/catch :
function loadConfig(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.warn('Configuration invalide, utilisation des valeurs par défaut');
return { theme: 'dark', language: 'en' };
}
}3. Ne pas capturer les erreurs que tu ne peux pas gérer — laisse-les remonter vers un gestionnaire de niveau supérieur :
function readFile(path) {
// Si ça échoue, laisse l'appelant le gérer
// Ne capture pas et ne masque pas silencieusement l'erreur
return fs.readFileSync(path, 'utf8');
}4. Relancer les erreurs quand tu as besoin de journaliser mais pas de les gérer complètement :
try {
processPayment(order);
} catch (error) {
console.error('Paiement échoué:', error);
throw error; // relancer pour que l'appelant la gère
}5. Limites d'erreur — dans des frameworks comme React, tu peux capturer les erreurs de rendu au niveau d'un composant afin qu'un composant cassé ne plante pas toute l'application. Le concept s'applique largement : capture les erreurs aux limites appropriées dans ton application.
<div id="output"></div>
<script>
function safeParseJSON(str) {
try {
const data = JSON.parse(str);
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
}
// Test avec JSON valide
const valid = safeParseJSON('{"name": "Alice", "age": 30}');
// Test avec JSON invalide
const invalid = safeParseJSON('not valid json');
const output = document.getElementById('output');
output.innerHTML = `
<p><strong>JSON valide:</strong> ${valid.success ? valid.data.name : 'Échoué'}</p>
<p><strong>JSON invalide:</strong> ${invalid.success ? 'Analysé' : invalid.error}</p>
`;
</script>Quand le bloc finally s'exécute-t-il ?