Aller au contenu principal

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 / catch pour 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...

  1. exécuter fermetures.js et lire les trois valeurs affichées
  2. dire à quoi sert la fonction ecrire dans creerEtat
  3. expliquer pourquoi afficher() affiche Score : 0 alors que scoreActuel vaut 5
📌 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é.

Bonne pratique - Un message qui aide

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 :

  1. le code du try s'exécute ligne par ligne ;
  2. à la première erreur, il s'arrête immédiatement ;
  3. le code du catch s'exécute, avec l'erreur dans la variable erreur ;
  4. 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 :

  1. informer l'utilisateur d'un message compréhensible ;
  2. journaliser l'erreur (console.error(erreur)) pour que le développeur la voie ;
  3. 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​


Quelle instruction lève une erreur avec un message explicite ?


Que fait le programme dès qu'une instruction du bloc try lève une erreur ?


Quand le bloc finally est-il exécuté ?


Pourquoi écrire erreur instanceof Error ? erreur.message : "Erreur inconnue" ?


Quel est le défaut du code catch (erreur) { } (bloc catch vide) ?


Que renvoie JSON.parse("pas du json") ?



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.


Bonne pratique - Valider tôt, valider tout

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).


Bonne pratique - Réagir, pas seulement attraper

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.


Bonne pratique - Documenter la valeur de secours

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.


Bonne pratique - finally contre les chargements infinis

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​

NotionRésumé
throw new Error("...")Lève une erreur avec un message clair (quoi, quelle valeur, ce qui était attendu).
try / catchLe code du try s'arrête à la première erreur, le catch la traite et le programme continue.
instanceof ErrorVérifie qu'on peut lire .message sans risque.
finallyToujours exécuté : idéal pour arrêter un indicateur de chargement.
Données externesElles 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 ?