Polar Code 🎭

Command Palette

Search for a command to run...

09
Pièce N°09

Module 9 -- Gestion des Erreurs et Asynchronisme : Le Sang-Froid et la Patience

Deux choses qui séparent l'amateur du pro : Savoir quand ça va exploser, et savoir attendre sans bloquer la scène. Les erreurs, c'est inévitable. L'asynchrone, c'est la vie réelle du web. Maîtrise-les, ou elles te maîtriseront.


9.1 Gestion des Erreurs - Attraper la Balle avant qu'elle ne Tue

Bloc try...catch...finally - Le Filet de Sécurité

// Sans filet
functioncoder(code) {
  return JSON.parse(code); // Boom si le code est pourri
}

// Avec filet
functioncoderSansExploser(code) {
  try {
    // Code dangereux
    return JSON.parse(code);
  } catch (erreur) {
    // Ce qui se passe si ça plante
    console.error("Code corrompu :", erreur.message);
    return null; // On rend quelque chose de sûr
  } finally {
    // S'exécute TOUJOURS, erreur ou pas
    console.log("Tentative de décodage terminée.");
  }
}

let message = '{mauvais: json}';
let résultat = décoderSansExploser(message); // Pas de crash

L'Objet Error - Le Cadavre de l'Erreur

try {
  throw new Error("C'est foutu."); // Lancer une erreur manuellement
} catch (e) {
  console.log(e.name);     // "Error"
  console.log(e.message);  // "C'est foutu."
  console.log(e.stack);    // La pile d'appels (où ça a merdé)
  
  // L'erreur est un objet comme un autre
  e.heure = new Date();
  e.niveau = "critique";
  console.log(e);
}

Types d'Erreurs Natifs - Connaître l'Ennemi

// SyntaxError : Le code lui-même est mal écrit.
// let 123bad = "test"; // ❌ SyntaxError

// TypeError : Opération sur un type incorrect.
let rien;
// rien.toUpperCase(); // ❌ TypeError: Cannot read properties of undefined

// ReferenceError : Variable qui n'existe pas.
// console.log(fantôme); // ❌ ReferenceError: fantôme is not defined

// RangeError : Valeur hors limites.
// let arr = new Array(-1); // ❌ RangeError: Invalid array length

// URIError : Mauvais encodage d'URL.
// decodeURIComponent('%'); // ❌ URIError: URI malformed

// En pratique
functionrifierCible(cible) {
  if (!cible) {
    throw new TypeError("Cible manquante.");
  }
  if (cible.length < 3) {
    throw new RangeError("Nom de cible trop court.");
  }
  // ...
}

try {
  vérifierCible("X");
} catch (e) {
  if (e instanceof TypeError) {
    console.log("Problème de type :", e.message);
  } else if (e instanceof RangeError) {
    console.log("Problème de limites :", e.message);
  }
}

Erreurs Personnalisées - Marquer Ton Territoire

class ErreurSécurité extends Error {
  constructor(message, code, ...params) {
    super(message, ...params);
    this.name = "ErreurSécurité";
    this.code = code;
    this.date = new Date();
  }
}

function accèsZoneRestreinte(niveau) {
  if (niveau < 7) {
    throw new ErreurSécurité("Niveau d'accès insuffisant.", "E403");
  }
  return "Accès accordé.";
}

try {
  accèsZoneRestreinte(5);
} catch (e) {
  if (e instanceof ErreurSécurité) {
    console.error(`[${e.code}] ${e.name} : ${e.message} (${e.date})`);
    // "[E403] ErreurSécurité : Niveau d'accès insuffisant. (Mon Mar 17...)"
  }
}

Bonnes Pratiques - Ne Pas Mourir Idiotement

// 1. TOUJOURS attraper les erreurs des promesses (on y vient)
// 2. Être spécifique dans les catch
try {
  // ...
} catch (e) {
  if (e instanceof MonErreur) {
    // Gérer précisément
  } else {
    // Relancer pour un autre niveau
    throw e;
  }
}

// 3. Nettoyer dans finally
let ressource;
try {
  ressource = acquérirRessource();
  // ... utiliser
} finally {
  if (ressource) {
    libérerRessource(ressource); // TOUJOURS exécuté
  }
}

// 4. Valider tôt, échouer tôt
function traiterDonnées(données) {
  if (!données || typeof données !== 'object') {
    throw new TypeError("Données invalides.");
  }
  // Le reste est plus sûr...
}

9.2 Asynchronisme - Attendre Sans Perdre le Fil

Nature Asynchrone de JavaScript - Le Monde Réel

console.log("Début de la surveillance.");

setTimeout(() => {
  console.log("Cible en mouvement."); // Ça arrive PLUS TARD
}, 1000);

console.log("Surveillance en cours...");
// Sortie :
// "Début de la surveillance."
// "Surveillance en cours..."
// (1 seconde plus tard) "Cible en mouvement."

// JavaScript ne BLOQUE PAS. Il met en attente et continue.

Callbacks et l'Enfer des Callbacks (Callback Hell)

// Un callback : fonction passée en argument, appelée PLUS TARD.
function observer(cible, callback) {
  setTimeout(() => {
    callback(`${cible} repérée.`);
  }, 500);
}

observer("Alpha", (message) => {
  console.log(message);
  observer("Beta", (message2) => {
    console.log(message2);
    observer("Gamma", (message3) => {
      console.log(message3);
      // Et ainsi de suite... L'ENFER.
      // Indentation à l'infini, gestion d'erreur impossible.
    });
  });
});

Promesses - Une Meilleure Attente

// Une Promesse est un objet qui représente une tâche future.
// Elle peut être : en attente (pending), tenue (fulfilled), ou rompue (rejected).

function observerPromesse(cible) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.2) { // 80% de chance de succès
        resolve(`${cible} localisée avec succès.`);
      } else {
        reject(new Error(`Perte de contact avec ${cible}.`));
      }
    }, 500);
  });
}

// Utilisation avec .then() et .catch()
observerPromesse("Alpha")
  .then(message => {
    console.log("Succès :", message);
    return observerPromesse("Beta"); // Chaînage
  })
  .then(message => {
    console.log("Succès :", message);
  })
  .catch(erreur => {
    console.error("Échec :", erreur.message);
  })
  .finally(() => {
    console.log("Opération terminée."); // Toujours exécuté
  });

Méthodes Utiles des Promesses

// Promise.all : Attendre que TOUTES les promesses soient tenues.
let missions = [
  observerPromesse("Cible A"),
  observerPromesse("Cible B"),
  observerPromesse("Cible C")
];

Promise.all(missions)
  .then(résultats => {
    console.log("Toutes cibles localisées :", résultats);
  })
  .catch(erreur => {
    // Échoue si UNE SEULE promesse est rompue
    console.error("Une mission a échoué :", erreur.message);
  });

// Promise.race : La première qui termine (succès ou échec).
Promise.race(missions)
  .then(premierRésultat => {
    console.log("Première localisation :", premierRésultat);
  });

// Promise.allSettled : Attendre que TOUTES soient terminées (succès ou échec).
Promise.allSettled(missions)
  .then(résultats => {
    résultats.forEach((r, i) => {
      if (r.status === 'fulfilled') {
        console.log(`Mission ${i} : ${r.value}`);
      } else {
        console.log(`Mission ${i} échouée : ${r.reason.message}`);
      }
    });
  });

async/await - L'Asynchrone qui a l'Air Synchrone

// `async` transforme une fonction pour qu'elle retourne une Promesse.
// `await` met en pause l'exécution jusqu'à ce qu'une Promesse soit tenue.

async fonction deSurveillance() {
  try {
    console.log("Lancement de la surveillance...");
    
    const résultat1 = await observerPromesse("Nord"); // Attend ici
    console.log(résultat1);
    
    const résultat2 = await observerPromesse("Sud");
    console.log(résultat2);
    
    const [résultat3, résultat4] = await Promise.all([
      observerPromesse("Est"),
      observerPromesse("Ouest")
    ]);
    console.log("Est et Ouest :", résultat3, résultat4);
    
    return "Surveillance complète.";
  } catch (erreur) {
    console.error("Panne dans la surveillance :", erreur.message);
    throw erreur; // Relance l'erreur
  }
}

// Appel
deSurveillance()
  .then(message => console.log(message))
  .catch(e => console.error("Échec global :", e.message));

// ATTENTION : `await` ne marche QUE dans une fonction `async`.

Gestion d'Erreurs avec async/await

// Méthode 1 : try/catch classique (recommandé)
async fonction mission() {
  try {
    let info = await obtenirInfoSecrète();
    let décodé = awaitcoder(info);
    return décodé;
  } catch (e) {
    console.error("Mission compromise :", e);
    return null; // Valeur par défaut
  }
}

// Méthode 2 : Attraper chaque promesse individuellement
async fonction missionRobuste() {
  let info = await obtenirInfoSecrète().catch(e => {
    console.error("Impossible d'obtenir l'info :", e);
    return "valeur_par_défaut";
  });
  
  let décodé = awaitcoder(info).catch(e => {
    console.error("Décodage échoué :", e);
    return "indéchiffrable";
  });
  
  return décodé;
}

// Méthode 3 : wrapper utilitaire
function attendreSansPeur(promise) {
  return promise
    .then(data => ({ data, error: null }))
    .catch(error => ({ data: null, error }));
}

async fonction missionSécurisée() {
  let { data: info, error: err1 } = await attendreSansPeur(obtenirInfoSecrète());
  if (err1) {
    // Gérer l'erreur sans try/catch
    return "échec_étape_1";
  }
  
  let { data: décodé, error: err2 } = await attendreSansPeur(décoder(info));
  if (err2) {
    return "échec_étape_2";
  }
  
  return décodé;
}

Résumé de ce chapitre sombre :

Pour les Erreurs :

  1. try/catch/finally : Ton filet de sécurité. Utilise-le pour le code qui peut exploser.
  2. Types d'erreurs : Connais les différents types pour mieux les traiter.
  3. Erreurs personnalisées : Crée les tiennes pour des messages et codes clairs.
  4. Bonnes pratiques : Valide tôt, nettoie toujours, sois spécifique.

Pour l'Asynchrone :

  1. Callbacks : L'ancienne méthode. Évite l'enfer des imbrications.
  2. Promesses : L'objet qui représente un futur résultat. Utilise .then(), .catch(), .finally().
  3. Promise.all/.race/.allSettled : Pour coordonner plusieurs promesses.
  4. async/await : La syntaxe moderne qui rend l'asynchrone lisible. Utilise try/catch avec.

La philosophie :

  • Erreurs : Assume qu'elles arriveront. Attrape-les, logue-les, gère-les proprement.
  • Asynchrone : Le monde n'attend pas. Apprends à gérer l'attente sans bloquer le reste.

Maîtrise ces deux aspects, et ton code survivra à la nuit.