Un programme qui tourne parfaitement sur votre machine peut très bien échouer chez un utilisateur : un serveur qui ne répond pas, une réponse mal formée, un fichier absent. Ces situations ne sont pas des bugs à corriger, ce sont des événements à prévoir.
C'est exactement le rôle de try : délimiter la portion de code où une erreur est envisagée, pour que le programme sache quoi faire au lieu de s'arrêter net.
Définition
try ouvre un bloc surveillé. Si une erreur est levée à l'intérieur, JavaScript abandonne immédiatement le reste du bloc et saute au catch qui le suit, en lui transmettant l'objet d'erreur.
try {
const donnees = JSON.parse('{ "prix": ');
console.log(donnees.prix); // cette ligne n'est jamais atteinte
} catch (erreur) {
console.log("Réponse illisible :", erreur.message);
}Un try ne vit jamais seul. Il doit être suivi d'un catch, d'un finally, ou des deux à la suite. Écrit isolément, il produit une erreur de syntaxe.
Ce qu'il surveille vraiment
La surveillance couvre l'exécution immédiate du bloc, y compris les fonctions appelées depuis ce bloc, aussi profond que descende la pile d'appels.
| Situation | Rattrapé par le try |
|---|---|
| Erreur levée par une ligne du bloc | Oui |
| Erreur levée dans une fonction appelée par le bloc | Oui |
| Erreur dans une fonction confiée à setTimeout | Non |
| Promesse rejetée sans await | Non |
Les deux dernières lignes expliquent la grande majorité des blocs qui semblent ne servir à rien. Quand la fonction différée s'exécute, le bloc a fini son travail depuis longtemps. Une Promise rejetée, elle, se rattrape en plaçant l'attente à l'intérieur du bloc, ou par sa méthode catch().
Une erreur de syntaxe dans le fichier n'est jamais rattrapable par un try : elle est détectée à l'analyse, avant la première ligne exécutée.
Un bloc étroit vaut mieux qu'un bloc large
La tentation est d'entourer trente lignes d'un seul try et de considérer le sujet réglé. Le résultat est un filet qui attrape aussi les fautes de programmation : un nom de propriété mal orthographié, un appel à une méthode inexistante. Ces erreurs méritent de remonter et d'être corrigées, pas d'être absorbées en silence.
const texteRecu = "{ ceci n'est pas du JSON }";
let config;
try {
config = JSON.parse(texteRecu);
} catch {
config = { theme: "clair" };
}
console.log(config.theme); // "clair"Le bloc n'entoure ici que l'appel qui peut réellement échouer, et la suite du traitement est écrite normalement, en dehors. Ce découpage a un second avantage : le catch sait ce qui a échoué, donc il peut décider quelque chose d'utile.
Questions fréquentes
Un try ralentit-il le code ?
Tant qu'aucune erreur n'est levée, le coût est négligeable dans une application ordinaire. Ce qui coûte réellement, c'est la levée elle-même : le moteur doit construire un objet Error et capturer la pile d'appels. Une boucle qui échoue à chaque tour est donc à éviter, mais le bloc en lui-même n'est pas le problème.
Peut-on imbriquer plusieurs try ?
Oui, et c'est courant dès qu'une reprise sur erreur peut elle-même échouer. Le bloc le plus proche traite l'erreur en premier. S'il ne sait pas quoi en faire, il la relance avec throw et elle poursuit sa route vers le bloc englobant. Évitez tout de même d'empiler trois niveaux : au-delà de deux, le chemin de l'erreur devient très difficile à suivre.
Faut-il en mettre autour de chaque appel réseau ?
Autour de chaque appel dont l'échec change le comportement de la page, oui. Un appel réseau échoue pour des raisons qui n'ont rien à voir avec le code : coupure de connexion, serveur indisponible, délai dépassé. Sans bloc surveillé, ces cas remontent en erreur non gérée et l'interface reste figée sur un écran de chargement.