Erreurs et try/catch
Ne jamais faire confiance à une donnée venue de l'extérieur : la vérifier, lever une erreur claire, et l'attraper proprement.
Objectifs de la séance
- lever une erreur avec
throw new Error(...); - attraper une erreur avec
try / catchpour que le programme continue ; - exécuter du code de nettoyage avec
finally; - lire un message d'erreur de façon sûre (
erreur instanceof Error) ; - sécuriser la lecture d'un texte JSON ;
- savoir quand ne pas attraper une erreur.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- exécuter
fermetures.jset lire les trois valeurs affichées - dire à quoi sert la fonction
ecriredanscreerEtat - expliquer pourquoi
afficher()afficheScore : 0alors quescoreActuelvaut5
📌 Réponse attendue
Notions théoriques
Lever une erreur avec throw
Une fonction qui reçoit une donnée inacceptable ne doit pas continuer en silence. Elle lève une erreur avec throw new Error("message") : le
programme s'arrête à cet endroit (sauf si quelqu'un attrape l'erreur) avec un message explicite.
function convertirNiveau(saisie) {
const niveau = Number(saisie);
if (!Number.isInteger(niveau) || niveau < 1 || niveau > 3) {
throw new Error(`Niveau invalide : "${saisie}" (attendu : 1, 2 ou 3)`);
}
return niveau;
}
console.log(convertirNiveau("2")); // 2
console.log(convertirNiveau("abc"));
2
Error: Niveau invalide : "abc" (attendu : 1, 2 ou 3)
Le deuxième appel arrête le programme : tout ce qui est écrit après n'est pas exécuté.
Un bon message d'erreur dit quoi (niveau invalide), quelle valeur a posé problème ("abc") et ce qui était attendu (1, 2 ou 3). Dans six mois, vous remercierez celui qui l'a écrit.
Attraper une erreur avec try / catch
Pour que le programme continue malgré l'erreur, entourez le code risqué d'un try, et décrivez la réaction dans le catch.
try {
const niveau = convertirNiveau("abc"); // lève une erreur
console.log("Niveau choisi :", niveau); // jamais exécuté
} catch (erreur) {
console.log("Impossible de continuer :", erreur.message);
}
console.log("Le programme continue.");
Impossible de continuer : Niveau invalide : "abc" (attendu : 1, 2 ou 3)
Le programme continue.
Le déroulement :
- le code du
trys'exécute ligne par ligne ; - à la première erreur, il s'arrête immédiatement ;
- le code du
catchs'exécute, avec l'erreur dans la variableerreur; - le programme continue après le
try/catch.
Lire le message en toute sécurité
En JavaScript, on peut lever n'importe quoi (throw "oups"). Ce que reçoit le catch n'est donc pas toujours un objet Error. Avant de lire .message,
vérifiez avec instanceof Error :
try {
convertirNiveau("abc");
} catch (erreur) {
const message = erreur instanceof Error ? erreur.message : "Erreur inconnue";
console.log(message);
}
Cette habitude vous évitera un message undefined (et, avec TypeScript, une erreur de type : vous le verrez en partie 6).
finally : toujours exécuté
Le bloc finally s'exécute dans tous les cas, qu'il y ait une erreur ou non. Il sert à nettoyer : arrêter un indicateur de chargement, fermer une connexion.
let enCours = false;
function lancerChargement(saisie) {
enCours = true; // « chargement en cours » (par exemple un cercle qui tourne)
try {
return convertirNiveau(saisie);
} catch (erreur) {
return null;
} finally {
enCours = false; // exécuté même si une erreur s'est produite
}
}
console.log(lancerChargement("abc")); // null
console.log(enCours); // false
Sans finally, une erreur dans le try laisserait enCours à true pour toujours : l'application afficherait un cercle de chargement éternel.
Les données externes peuvent être invalides
Toute donnée qui vient de l'extérieur (formulaire, fichier, réseau, stockage) peut être incorrecte. JSON.parse en est l'exemple le plus courant :
const bon = JSON.parse('{"pseudo": "Alice", "score": 12}');
console.log(bon.pseudo); // Alice
const mauvais = JSON.parse("pas du json"); // SyntaxError : le programme s'arrête
La version sécurisée renvoie null quand le texte est invalide, et celui qui l'appelle doit tester ce cas :
function lireJson(texte) {
try {
return JSON.parse(texte);
} catch {
return null; // texte invalide : on le signale par null
}
}
console.log(lireJson('{"score": 12}')); // { score: 12 }
console.log(lireJson("pas du json")); // null
Quand ne pas attraper une erreur
Un catch vide (ou qui ignore l'erreur) cache les bugs.
try {
faireQuelqueChose();
} catch (erreur) {
// MAUVAIS : ne rien faire rend le bug invisible !
}
Dans un catch, faites toujours au moins une de ces trois choses :
- informer l'utilisateur d'un message compréhensible ;
- journaliser l'erreur (
console.error(erreur)) pour que le développeur la voie ; - renvoyer une valeur de secours explicite (
null, liste vide...) que l'appelant saura traiter.
N'attrapez que les erreurs que vous savez gérer. Les bugs de programmation doivent rester visibles pendant le développement.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Votre mission : sécuriser la lecture du niveau et d'un joueur sauvegardé. Travaillez dans le fichier labo-js/erreurs.js.
Étape 1 — Lever une erreur claire
Complétez convertirNiveau : si le niveau n'est pas un entier entre 1 et 3, levez une erreur dont le message contient la saisie.
Refusez une donnée invalide dès qu'elle entre dans votre code. Plus une valeur incorrecte voyage loin, plus le bug sera difficile à retrouver.
Étape 2 — Attraper l'erreur
Appelez convertirNiveau("abc") dans un try, et affichez le message de l'erreur dans le catch (avec instanceof Error).
Ici, le catch affiche un message. Dans une application, il afficherait une phrase pour l'utilisateur (« Choisissez un niveau entre 1 et 3 ») et laisserait le niveau précédent.
Étape 3 — Sécuriser un JSON
Un joueur est sauvegardé sous forme de texte JSON. Écrivez lireJoueur(texte), qui renvoie l'objet, ou null si le texte est invalide.
null est une valeur de secours explicite : celui qui appelle lireJoueur doit tester if (joueur === null). C'est bien différent d'un catch vide qui ne renvoie rien.
Étape 4 — Toujours remettre l'indicateur à zéro
Complétez la fonction pour que enCours redevienne false quel que soit le résultat.
Dans une application, enCours = false dans un finally garantit que le cercle de chargement disparaît même en cas d'échec. Vous retrouverez ce motif exact dans la partie 5.
📌 Une solution
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
throw new Error("...") | Lève une erreur avec un message clair (quoi, quelle valeur, ce qui était attendu). |
try / catch | Le code du try s'arrête à la première erreur, le catch la traite et le programme continue. |
instanceof Error | Vérifie qu'on peut lire .message sans risque. |
finally | Toujours exécuté : idéal pour arrêter un indicateur de chargement. |
| Données externes | Elles peuvent être invalides : JSON.parse et les conversions se protègent. |
catch vide | À proscrire : informer, journaliser ou renvoyer une valeur de secours explicite. |
Aperçu de la prochaine séance
Votre code sait ranger ses fichiers, mémoriser des valeurs et gérer les erreurs. Dans la partie 5, vous attaquez un morceau important : le code asynchrone. Comment attendre la réponse d'un serveur sans bloquer l'application, et sans bug si elle arrive en retard ou dans le désordre ?