Introduction : Pourquoi la gestion des erreurs est le fondement d'un code fiable
Les erreurs dans le code sont inévitables. Elles surviennent à cause de données utilisateur incorrectes, de pannes réseau, de fichiers absents sur le serveur ou de simples fautes de frappe du développeur. Dans l'environnement Node.js, où le code s'exécute de manière asynchrone et interagit souvent avec des ressources externes (bases de données, système de fichiers, API), une erreur non traitée peut entraîner l'arrêt complet du processus. Une gestion compétente des erreurs en JavaScript transforme une application fragile en un service robuste. Dans cet article, nous allons passer en revue tous les mécanismes clés : du classique try/catch à la création de classes d'erreurs personnalisées et au travail avec le code asynchrone.
1. Les bases : try, catch, finally et throw
Le bloc de base pour la gestion des exceptions en JavaScript est la construction try...catch...finally. Elle permet d'"attraper" une erreur survenue dans une section "dangereuse" du code et d'y réagir correctement sans interrompre l'exécution de tout le programme.
Syntaxe et fonctionnement de try/catch
Le code susceptible de lever une exception est placé dans le bloc try. Si une erreur se produit, le contrôle est immédiatement transféré au bloc catch, où l'objet d'erreur est accessible. S'il n'y a pas d'erreur, le bloc catch est ignoré.
try { const data = JSON.parse('{"name": "Anna"}'); // JSON valide console.log(data.name); // Affiche : Anna} catch (error) { console.error('Erreur d\'analyse :', error.message);}// L'exécution continueSimulons maintenant une erreur :
try { const data = JSON.parse('json invalide'); // Erreur ! console.log(data.name);} catch (error) { console.error('Erreur d\'analyse :', error.message); // Affichage : Erreur d'analyse : Unexpected token 'i', ... is not valid JSON}console.log('Le programme est vivant !'); // Ce code s'exécuteraBloc finally : le code qui s'exécute toujours
Le bloc finally s'exécute indépendamment du fait qu'une erreur se soit produite ou non. Il est parfait pour libérer des ressources (fermeture de fichiers, connexions à la base de données) ou nettoyer des temporisateurs.
let resource = null;try { resource = acquireResource(); // On obtient la ressource resource.doSomething(); // Opération dangereuse} catch (error) { console.error('Erreur :', error);} finally { if (resource) { resource.close(); // On ferme toujours la ressource console.log('Ressource libérée.'); }}Opérateur throw : créer ses propres erreurs
Vous pouvez vous-même "lancer" une erreur avec throw. Cela est utile pour valider des données d'entrée ou interrompre l'exécution en cas de violation de la logique métier. Vous pouvez passer n'importe quoi comme argument, mais la bonne pratique est d'utiliser un objet Error ou ses descendants.
function divide(a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw new Error('Les arguments doivent être des nombres'); } if (b === 0) { throw new Error('La division par zéro est interdite'); } return a / b;}
try { console.log(divide(10, 0));} catch (error) { console.error('Erreur :', error.message); // Erreur : La division par zéro est interdite}2. Techniques avancées : Classes d'erreurs personnalisées et typage
Dans les grands projets, utiliser simplement l'objet Error standard ne suffit pas. Il faut distinguer les erreurs réseau, de validation, d'autorisation, etc. Pour cela, on crée des classes personnalisées qui étendent l'objet Error intégré.
Création d'erreurs personnalisées
class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; // Nom unique pour la vérification }}
class NetworkError extends Error { constructor(message, statusCode) { super(message); this.name = 'NetworkError'; this.statusCode = statusCode; }}
function fetchUser(id) { if (!id) { throw new Valida