Rattraper une erreur ne consiste pas à la faire disparaître. Un bloc catch qui se contente d'un commentaire poli transforme une panne visible en comportement inexplicable, ce qui est nettement pire.
Le vrai sujet de ce mot-clé n'est donc pas la syntaxe, qui tient en trois lignes, mais la décision : que faire de cette erreur maintenant qu'elle est entre les mains du programme.
Définition
catch ferme un bloc try et reçoit l'objet lancé par l'erreur. Le code qu'il contient ne s'exécute que si quelque chose a échoué, et l'exécution reprend normalement juste après, comme si de rien n'était.
function lireConfig(texte) {
try {
return JSON.parse(texte);
} catch (erreur) {
console.warn("Configuration illisible :", erreur.message);
return { theme: "clair" };
}
}
console.log(lireConfig('{ "theme": "sombre" }')); // { theme: 'sombre' }
console.log(lireConfig("cassé")); // { theme: 'clair' }Cette fonction ne peut plus échouer du point de vue de celui qui l'appelle : elle renvoie toujours un objet exploitable. C'est la forme la plus utile d'un rattrapage, celle qui produit une valeur de repli au lieu de propager le problème.
Ce que contient l'objet d'erreur
Le paramètre reçu est presque toujours une instance de Error, avec un petit nombre de propriétés standard.
| Propriété | Ce qu'elle contient |
|---|---|
name | Le type, par exemple TypeError ou SyntaxError |
message | La phrase lisible qui décrit le problème |
stack | La pile d'appels au moment de la levée |
cause | L'erreur d'origine, si elle a été transmise |
Le mot presque a son importance : throw accepte n'importe quelle valeur, y compris une chaîne. Un bloc qui écrit erreur.message sans réfléchir affiche donc parfois undefined. Un test avec instanceof lève le doute quand la source n'est pas votre propre code.
Distinguer plusieurs erreurs
JavaScript n'offre pas plusieurs blocs par type, contrairement à d'autres langages. Le tri se fait à l'intérieur, avec une condition ordinaire.
try {
JSON.parse("{{{");
} catch (erreur) {
if (erreur instanceof SyntaxError) {
console.log("Format invalide");
} else {
throw erreur; // ce bloc ne sait pas traiter le reste
}
}La branche else est la partie que l'on oublie le plus souvent. Sans elle, le bloc avale silencieusement des erreurs qu'il ne comprend pas, et le défaut réapparaît trois écrans plus loin sous une forme méconnaissable. Relancer ce que l'on ne sait pas traiter est la règle par défaut.
Depuis 2019, le paramètre est facultatif : catch { sans parenthèses est valide quand l'objet d'erreur ne sert à rien.
Questions fréquentes
Que se passe-t-il si le catch lève lui-même une erreur ?
Elle sort de la structure et remonte vers le bloc englobant, exactement comme si elle avait été levée en dehors. Le finally associé, lui, s'exécute quand même avant ce départ. C'est le mécanisme qui permet de relancer une erreur enrichie sans perdre le nettoyage prévu.
Peut-on rattraper une erreur dans une promesse ?
Oui, de deux façons. Avec async et await, la structure classique fonctionne telle quelle, puisque l'attente ramène l'échec dans le fil courant. Sans elle, il faut passer par la méthode catch() de la Promise. Mélanger les deux styles sur le même appel ne sert à rien.
Faut-il journaliser dans chaque bloc ?
Non, et c'est une source de bruit considérable. Journalisez à l'endroit où la décision est prise, une seule fois par erreur. Si le bloc relance, il ne journalise pas : le niveau supérieur le fera avec plus de contexte, et deux lignes pour un même incident font perdre du temps le jour où il faut comprendre ce qui s'est passé.