Cours JavaScript et TypeScript · Module 4: JavaScript asynchrone
Chaînage de Promises (then, catch, finally)
Dans cette leçon12
Dans l'exercice précédent, nous avons appris les bases de la création et de l'utilisation des objets Promise. Plongeons maintenant plus en profondeur dans les techniques de chaînage des promises pour construire des flux asynchrones plus complexes. Dans un système aussi complexe que le Parc Jurassique, nous devons souvent exécuter des séquences d'opérations asynchrones, gérer les erreurs et garantir le bon fonctionnement des systèmes.
Rappel des méthodes de Promise
Avant de passer aux techniques avancées, rappelons les méthodes de base de l'objet Promise :
then(onFulfilled, onRejected)- enregistre des gestionnaires pour les états résolu et rejetécatch(onRejected)- enregistre un gestionnaire uniquement pour l'état rejeté (raccourci de.then(null, onRejected))finally(onFinally)- enregistre un gestionnaire qui s'exécute quel que soit le résultat
Chacune de ces méthodes retourne une nouvelle Promise, ce qui constitue le fondement du chaînage.
Comment fonctionne le chaînage
Lorsque tu appelles .then() sur une Promise, la valeur retournée par le callback détermine l'état de la nouvelle Promise :
1Promise.resolve("départ")
2 .then(value => {
3 console.log(value); // "départ"
4 return "deuxième étape"; // valeur simple → la Promise suivante se résout avec elle
5 })
6 .then(value => {
7 console.log(value); // "deuxième étape"
8 return Promise.resolve("troisième étape"); // Promise → la Promise suivante se résout avec sa valeur
9 })
10 .then(value => {
11 console.log(value); // "troisième étape"
12 throw new Error("quelque chose a mal tourné"); // throw → la Promise suivante se rejette
13 })
14 .catch(error => {
15 console.error(error.message); // "quelque chose a mal tourné"
16 return "récupéré"; // return dans catch → la chaîne continue
17 })
18 .then(value => {
19 console.log(value); // "récupéré"
20 });Opérations séquentielles - pipeline du labo de dinosaures
Construisons un pipeline complet de création de dinosaure dans notre labo du Parc Jurassique :
1// Fonctions d'étape - chacune retourne une Promise
2function collectDNASample(dinosaurId) {
3 return new Promise((resolve, reject) => {
4 setTimeout(() => {
5 if (dinosaurId > 0) {
6 resolve({ id: dinosaurId, dna: `ACGT-${dinosaurId}-TGCA`, quality: "high" });
7 } else {
8 reject(new Error("ID de dinosaure invalide pour la collecte d'ADN"));
9 }
10 }, 800);
11 });
12}
13
14function sequenceDNA(sample) {
15 return new Promise((resolve, reject) => {
16 setTimeout(() => {
17 if (sample.quality === "high") {
18 resolve({ ...sample, sequence: `SEQ-${sample.dna}-END`, confidence: 0.98 });
19 } else {
20 reject(new Error(`Qualité d'ADN trop faible : ${sample.quality}`));
21 }
22 }, 1200);
23 });
24}
25
26function synthesizeProtein(sequencedDNA) {
27 return new Promise(resolve => {
28 setTimeout(() => {
29 resolve({
30 ...sequencedDNA,
31 protein: `PROTEIN-${sequencedDNA.id}`,
32 aminoAcids: 847
33 });
34 }, 1000);
35 });
36}
37
38function createEmbryo(proteinData) {
39 return new Promise((resolve, reject) => {
40 setTimeout(() => {
41 const success = Math.random() > 0.2;
42 if (success) {
43 resolve({ embryoId: `EMB-${proteinData.id}`, viability: "high", source: proteinData });
44 } else {
45 reject(new Error("Échec de la création de l'embryon - protéine insuffisante"));
46 }
47 }, 1500);
48 });
49}
50
51// Pipeline complet
52console.log("=== Labo ADN du Parc Jurassique - Démarrage du pipeline ===");
53
54collectDNASample(7)
55 .then(sample => {
56 console.log(`Étape 1 - ADN collecté : ${sample.dna} (qualité : ${sample.quality})`);
57 return sequenceDNA(sample);
58 })
59 .then(sequenced => {
60 console.log(`Étape 2 - ADN séquencé : ${sequenced.sequence} (confiance : ${sequenced.confidence})`);
61 return synthesizeProtein(sequenced);
62 })
63 .then(protein => {
64 console.log(`Étape 3 - Protéine synthétisée : ${protein.protein} (${protein.aminoAcids} acides aminés)`);
65 return createEmbryo(protein);
66 })
67 .then(embryo => {
68 console.log(`Étape 4 - Embryon créé : ${embryo.embryoId} (viabilité : ${embryo.viability})`);
69 console.log("Pipeline terminé ! Embryon prêt pour l'incubation.");
70 return embryo;
71 })
72 .catch(error => {
73 console.error(`Échec du pipeline : ${error.message}`);
74 console.log("Lancement des procédures de nettoyage...");
75 })
76 .finally(() => {
77 console.log("=== Pipeline du labo terminé ===");
78 });Valeurs de retour de then() et catch()
La valeur retournée par un callback dans .then() ou .catch() devient la valeur avec laquelle se résout la Promise retournée par cette méthode. Cela nous permet de transformer les données à chaque étape de la chaîne.
1// Exemple de transformation de données dans une chaîne de Promises
2fetchDinosaurInfo(5)
3 .then(dinoData => {
4 // Transformer les données du dinosaure au format utilisé par l'UI
5 return {
6 id: dinoData.id,
7 name: dinoData.name || "Inconnu",
8 display: `${dinoData.species} (${dinoData.age} ans)`,
9 dangerLevel: dinoData.carnivore ? "Élevé" : "Faible",
10 location: dinoData.enclosureId
11 };
12 })
13 .then(uiData => {
14 // Ajouter des informations supplémentaires pour l'UI
15 return {
16 ...uiData,
17 icon: uiData.dangerLevel === "Élevé" ? "!!" : "OK",
18 description: `${uiData.icon} ${uiData.display} - Menace : ${uiData.dangerLevel}`
19 };
20 })
21 .then(finalData => {
22 console.log("Données pour l'UI :", finalData);
23 updateDinoCard(finalData); // Mettre à jour l'interface utilisateur
24 });Gestion des erreurs dans la chaîne de Promises
Il existe plusieurs stratégies pour gérer les erreurs dans les chaînes de Promises :
1. Un seul catch à la fin de la chaîne
1prepareTransport(7) // Le dinosaure ID 7 est trop grand pour un transport standard
2 .then(transportInfo => loadDinosaur(transportInfo))
3 .then(shipmentInfo => transportToPark(shipmentInfo))
4 .then(deliveryInfo => placeInEnclosure(deliveryInfo))
5 .then(enclosureInfo => {
6 console.log("Succès :", enclosureInfo);
7 })
8 .catch(error => {
9 // Ce catch gère les erreurs de toutes les étapes précédentes
10 console.error("Une erreur est survenue :", error.message);
11 notifyAdministration(error);
12 });2. Plusieurs catch pour différentes étapes
1prepareTransport(7)
2 .then(transportInfo => loadDinosaur(transportInfo))
3 .catch(error => {
4 // Ce catch ne gère que les erreurs de prepareTransport et loadDinosaur
5 console.error("Erreur pendant la préparation ou le chargement :", error.message);
6 return orderOversizedTransport(7); // Chemin alternatif - retourner une nouvelle Promise
7 })
8 .then(info => {
9 // Ce then reçoit soit le résultat de loadDinosaur, soit celui d'orderOversizedTransport
10 return transportToPark(info);
11 })
12 .then(deliveryInfo => placeInEnclosure(deliveryInfo))
13 .catch(error => {
14 // Ce catch gère les erreurs de transportToPark, placeInEnclosure ou orderOversizedTransport
15 console.error("Erreur pendant le transport ou le placement dans l'enclos :", error.message);
16 notifySecurityTeam(error);
17 throw error; // Propager l'erreur plus loin
18 })
19 .catch(error => {
20 // Ce catch intercepte toutes les erreurs non gérées
21 console.error("Erreur critique dans le processus :", error.message);
22 activateEmergencyProcedures();
23 });
24
25function orderOversizedTransport(dinoId) {
26 console.log(`Commande d'un transport hors gabarit pour le dinosaure n°${dinoId}...`);
27 // Implémentation retournant une Promise
28 return Promise.resolve({
29 id: dinoId,
30 vehicle: "Transport spécial renforcé XL",
31 status: "Préparé"
32 });
33}3. Gestion des erreurs à l'intérieur de la fonction then()
1prepareTransport(3)
2 .then(transportInfo => {
3 try {
4 // Code susceptible de lever des exceptions
5 validateTransport(transportInfo);
6 return loadDinosaur(transportInfo);
7 } catch (error) {
8 // Gestion des exceptions synchrones
9 console.error("Erreur de validation du transport :", error.message);
10 return repairTransport(transportInfo); // Retourner une nouvelle Promise
11 }
12 })
13 .then(shipmentInfo => transportToPark(shipmentInfo))
14 .catch(error => {
15 // Gestion des erreurs restantes
16 console.error("Erreur du processus :", error.message);
17 });
18
19function validateTransport(transportInfo) {
20 if (!transportInfo.vehicle.includes("cage")) {
21 throw new Error("Le transport ne dispose pas des mesures de sécurité adéquates !");
22 }
23}
24
25function repairTransport(transportInfo) {
26 console.log("Réparation des problèmes de transport...");
27 return Promise.resolve({
28 ...transportInfo,
29 vehicle: transportInfo.vehicle + " avec une cage renforcée",
30 status: "Réparé"
31 });
32}Transformation des erreurs
La méthode .catch() peut non seulement gérer une erreur, mais aussi la transformer en succès en retournant une valeur :
1fetchDinosaurInfo(999) // L'ID n'existe pas
2 .catch(error => {
3 console.log("Dinosaure introuvable :", error.message);
4 // Au lieu de propager l'erreur, retourner des données par défaut
5 return {
6 id: 999,
7 name: "Inconnu",
8 species: "Espèce inconnue",
9 age: "?",
10 carnivore: true,
11 enclosureId: "N/A"
12 };
13 })
14 .then(dinoData => {
15 // Ce then s'exécutera avec les données par défaut
16 console.log("Données du dinosaure (par défaut ou réelles) :", dinoData);
17 updateUI(dinoData);
18 });Imbriqué vs chaîné - une distinction essentielle
1// IMBRIQUÉ (mauvais) - crée un promise hell, la gestion des erreurs est désordonnée
2fetchDinosaurData(1).then(dino => {
3 fetchEnclosureData(dino.enclosureId).then(enclosure => {
4 fetchVetRecord(dino.id).then(record => {
5 // Les trois variables sont accessibles ici, mais le code est profondément imbriqué
6 console.log(dino.name, enclosure.name, record.lastCheckup);
7 }).catch(e => console.error("erreur véto", e));
8 }).catch(e => console.error("erreur enclos", e));
9}).catch(e => console.error("erreur dino", e));
10
11// CHAÎNÉ (bon) - structure plate, un seul gestionnaire d'erreurs
12let dinoRef, enclosureRef;
13
14fetchDinosaurData(1)
15 .then(dino => {
16 dinoRef = dino;
17 return fetchEnclosureData(dino.enclosureId);
18 })
19 .then(enclosure => {
20 enclosureRef = enclosure;
21 return fetchVetRecord(dinoRef.id);
22 })
23 .then(record => {
24 console.log(dinoRef.name, enclosureRef.name, record.lastCheckup);
25 })
26 .catch(error => {
27 // Intercepte toute erreur de n'importe quelle étape
28 console.error("Échec :", error.message);
29 });Chaînage conditionnel
Parfois, nous voulons sauter des étapes de façon conditionnelle :
1function processEmergencyReport(reportId, urgency) {
2 return fetchReport(reportId)
3 .then(report => {
4 console.log(`Rapport reçu : ${report.title}`);
5
6 // Ajouter conditionnellement une étape supplémentaire selon l'urgence
7 if (urgency === "critical") {
8 return notifyDirector(report).then(() => report); // Retourner le rapport après notification
9 }
10 return report; // Sauter la notification pour les cas non critiques
11 })
12 .then(report => {
13 return logReport(report); // Toujours journaliser
14 })
15 .then(() => {
16 console.log("Traitement du rapport terminé.");
17 });
18}
19
20// Fonctions auxiliaires
21function fetchReport(id) { return Promise.resolve({ id, title: `Rapport n°${id}`, data: {} }); }
22function notifyDirector(r) { return new Promise(res => setTimeout(() => { console.log("Directeur averti"); res(); }, 300)); }
23function logReport(r) { return new Promise(res => setTimeout(() => { console.log(`Journalisé : ${r.title}`); res(r); }, 200)); }
24
25processEmergencyReport(42, "critical");
26processEmergencyReport(43, "low");Utiliser finally pour le nettoyage
finally est parfait pour la logique de nettoyage qui doit s'exécuter quel que soit le succès ou l'échec :
1function runLabExperiment(experimentId) {
2 let labResources = null;
3
4 return allocateLabResources(experimentId)
5 .then(resources => {
6 labResources = resources;
7 console.log(`Ressources allouées : ${resources.resourceId}`);
8 return runExperiment(resources);
9 })
10 .then(result => {
11 console.log(`Expérience réussie : ${result.outcome}`);
12 return result;
13 })
14 .catch(error => {
15 console.error(`Échec de l'expérience : ${error.message}`);
16 throw error; // Relancer pour que l'appelant sache qu'elle a échoué
17 })
18 .finally(() => {
19 // Toujours libérer les ressources, même si l'expérience a échoué
20 if (labResources) {
21 releaseLabResources(labResources);
22 console.log("Ressources du labo libérées.");
23 }
24 });
25}
26
27function allocateLabResources(id) {
28 return Promise.resolve({ resourceId: `LAB-RES-${id}`, equipment: ["centrifugeuse", "séquenceur"] });
29}
30function runExperiment(resources) {
31 return new Promise((resolve, reject) => {
32 setTimeout(() => {
33 Math.random() > 0.3
34 ? resolve({ outcome: "ADN extrait avec succès" })
35 : reject(new Error("Contamination de l'expérience détectée"));
36 }, 1000);
37 });
38}
39function releaseLabResources(resources) {
40 console.log(`Libération de ${resources.resourceId}`);
41}
42
43runLabExperiment(101);Opérations complexes avec les Promises
Exécution séquentielle d'opérations asynchrones sur un tableau
Parfois, nous devons effectuer une opération asynchrone sur chaque élément d'un tableau, l'un après l'autre :
1// Nourrissage séquentiel des dinosaures
2const dinosaurIds = [1, 2, 3, 4, 5];
3
4// Fonction pour nourrir un dinosaure
5function feedDinosaur(id) {
6 return new Promise((resolve, reject) => {
7 console.log(`Début du nourrissage du dinosaure n°${id}...`);
8 setTimeout(() => {
9 if (id === 3) {
10 reject(new Error(`Le dinosaure n°${id} refuse de manger !`));
11 return;
12 }
13 console.log(`Dinosaure n°${id} nourri !`);
14 resolve({id, status: 'nourri', time: new Date().toISOString()});
15 }, 1000);
16 });
17}
18
19// Nourrissage séquentiel de tous les dinosaures
20dinosaurIds.reduce((promise, dinoId) => {
21 return promise
22 .then(results => {
23 // results est un tableau des résultats des opérations précédentes
24 return feedDinosaur(dinoId)
25 .then(result => {
26 // Ajouter le résultat au tableau de résultats
27 return [...results, result];
28 })
29 .catch(error => {
30 console.error(`Erreur lors du nourrissage du dinosaure n°${dinoId} :`, error.message);
31 // Ajouter les informations d'erreur aux résultats
32 return [...results, {id: dinoId, status: 'error', error: error.message}];
33 });
34 });
35}, Promise.resolve([])) // Démarrer avec un tableau de résultats vide
36 .then(allResults => {
37 console.log("Tous les dinosaures traités :");
38 console.log(allResults);
39
40 // Vérifier si tous les dinosaures ont été nourris
41 const allFed = allResults.every(result => result.status === 'nourri');
42 if (allFed) {
43 console.log("Tous les dinosaures ont été nourris !");
44 } else {
45 console.log("Tous les dinosaures n'ont pas été nourris - consulte le rapport.");
46 }
47 });Exécuter des opérations asynchrones avec un nombre limité de tâches parallèles
1// Fonction pour exécuter des opérations async avec un parallélisme limité
2async function executeWithLimit(array, promiseFunction, parallelLimit) {
3 const allResults = [];
4
5 // Exécuter les tâches par lots
6 for (let i = 0; i < array.length; i += parallelLimit) {
7 const batch = array.slice(i, i + parallelLimit);
8
9 // Lancer Promise.all pour un lot de tâches
10 const results = await Promise.all(
11 batch.map(element =>
12 promiseFunction(element)
13 .catch(error => {
14 console.error(`Erreur pour l'élément ${element} :`, error.message);
15 return { element, status: 'error', error: error.message };
16 })
17 )
18 );
19
20 allResults.push(...results);
21 console.log(`Lot ${i / parallelLimit + 1} terminé (${results.length} opérations)`);
22 }
23
24 return allResults;
25}
26
27// Utilisation de la fonction pour des vérifications de santé parallèles mais limitées
28const allDinosaurIds = Array.from({ length: 20 }, (_, i) => i + 1);
29
30function checkDinosaurHealth(id) {
31 return new Promise((resolve, reject) => {
32 console.log(`Vérification de la santé du dinosaure n°${id}...`);
33 setTimeout(() => {
34 // Simuler un état de santé aléatoire
35 const health = Math.floor(Math.random() * 100);
36 if (health < 30) {
37 reject(new Error(`Le dinosaure n°${id} a des problèmes de santé ! Niveau de santé : ${health}`));
38 return;
39 }
40 console.log(`Le dinosaure n°${id} est en bonne santé. Niveau de santé : ${health}`);
41 resolve({id, health, status: 'healthy'});
42 }, 500 + Math.random() * 1000); // Temps d'exécution aléatoire
43 });
44}
45
46// Vérifier la santé de 5 dinosaures au maximum à la fois
47executeWithLimit(allDinosaurIds, checkDinosaurHealth, 5)
48 .then(results => {
49 console.log("Vérification de la santé de tous les dinosaures terminée :");
50
51 const healthy = results.filter(w => w.status === 'healthy').length;
52 const sick = results.filter(w => w.status === 'error').length;
53
54 console.log(`En bonne santé : ${healthy}, Nécessitant une attention : ${sick}`);
55
56 // Afficher les dinosaures qui nécessitent une attention
57 if (sick > 0) {
58 console.log("Dinosaures nécessitant une intervention vétérinaire :");
59 results
60 .filter(w => w.status === 'error')
61 .forEach(w => console.log(`- Dinosaure n°${w.element} : ${w.error}`));
62 }
63 });Bonnes pratiques pour le chaînage de Promises
Retourne toujours une Promise depuis les fonctions then() - cela garantit un chaînage correct et la propagation des résultats :
1.then(data => { 2 // Mauvais - ne retourne pas de Promise, la chaîne sera rompue 3 processData(data); 4}) 5 6.then(data => { 7 // Bon - retourne le résultat du traitement au prochain then() 8 return processData(data); 9})Utilise un seul catch() pour un groupe d'opérations liées - cela rend le code plus lisible :
1fetchEnclosureData(5) 2 .then(data => updateEnclosureStatus(data)) 3 .then(status => notifyStaff(status)) 4 .catch(error => { 5 // Un seul catch pour toute l'opération de mise à jour de l'enclos 6 handleEnclosureError(error); 7 }); 8 9// Nouvelle opération non liée avec son propre catch 10fetchGuestList() 11 .then(visitors => updateVisitorCount(visitors)) 12 .catch(error => { 13 // catch séparé pour les opérations liées aux visiteurs 14 handleVisitorError(error); 15 });Utilise finally() pour le code de nettoyage - quel que soit le succès ou l'échec :
1openEnclosureGate(enclosureId) 2 .then(() => moveDinosaur(dinoId, enclosureId)) 3 .catch(error => { 4 logError("Erreur de déplacement :", error); 5 throw error; // Propager l'erreur plus loin 6 }) 7 .finally(() => { 8 // Toujours fermer la porte, quel que soit le résultat 9 closeEnclosureGate(enclosureId); 10 });Évite les chaînes de Promises profondément imbriquées - découpe-les en fonctions plus petites et nommées :
1// Au lieu d'une longue chaîne : 2function moveDinosaurToNewEnclosure(dinoId, newEnclosureId) { 3 return fetchDinosaurInfo(dinoId) 4 .then(dino => { 5 // Vérifier l'enclos actuel 6 return fetchEnclosureData(dino.enclosureId) 7 .then(oldEnclosure => { 8 // Préparer le nouvel enclos 9 return fetchEnclosureData(newEnclosureId) 10 .then(newEnclosure => { 11 // Vérifier la compatibilité 12 // ... encore plus de then imbriqués... 13 }); 14 }); 15 }); 16} 17 18// Mieux - découper en fonctions plus petites : 19function moveDinosaurToNewEnclosure(dinoId, newEnclosureId) { 20 return fetchDinosaurInfoWithCurrentEnclosure(dinoId) 21 .then(data => checkEnclosureCompatibility(data, newEnclosureId)) 22 .then(data => executeTransfer(data)); 23} 24 25function fetchDinosaurInfoWithCurrentEnclosure(dinoId) { 26 let dinoData; 27 return fetchDinosaurInfo(dinoId) 28 .then(dino => { 29 dinoData = dino; 30 return fetchEnclosureData(dino.enclosureId); 31 }) 32 .then(oldEnclosure => { 33 return { dino: dinoData, oldEnclosure }; 34 }); 35} 36 37function checkEnclosureCompatibility(data, newEnclosureId) { 38 return fetchEnclosureData(newEnclosureId) 39 .then(newEnclosure => { 40 // Vérifier la compatibilité 41 return { ...data, newEnclosure }; 42 }); 43} 44 45function executeTransfer(data) { 46 // Implémentation du transfert 47 return Promise.resolve({ status: "completed", ...data }); 48}
Résumé
Le chaînage des Promises avec .then(), .catch() et .finally() te donne des outils puissants pour construire des flux asynchrones complexes. Une chaîne de Promises bien conçue peut rendre lisibles et faciles à maintenir même des opérations asynchrones compliquées, comme la gestion du transport des dinosaures au Parc Jurassique.
Principes clés :
- Retourne toujours une Promise depuis les fonctions dans
.then()pour poursuivre la chaîne - Utilise
.catch()pour une gestion unifiée des erreurs - Utilise
.finally()pour les opérations de nettoyage - Évite d'imbriquer les Promises au profit du chaînage
- Découpe les opérations complexes en fonctions plus petites et nommées
Dans le prochain exercice, nous explorerons des méthodes plus avancées pour travailler simultanément avec plusieurs Promises, comme Promise.all(), Promise.race() et Promise.allSettled().
Code de cette leçon : index.js
1// Arrow Function Converter - Fonctions modernes
2console.log("ARROW FUNCTIONS IN JURASSIC PARK\n");
3
4// Fonction traditionnelle
5function calculateFeedingCost(dinosaurCount, costPerDinosaur) {
6 return dinosaurCount * costPerDinosaur;
7}
8
9// Function arrow - shorter syntax
10const calculateFeedingCostArrow = (dinosaurCount, costPerDinosaur) => {
11 return dinosaurCount * costPerDinosaur;
12};
13
14// Function arrow - implicit return (without {})
15const calculateFeedingCostShort = (dinosaurCount, costPerDinosaur) =>
16 dinosaurCount * costPerDinosaur;
17
18console.log("=== SYNTAX COMPARISON ===\n");
19
20const trexCount = 3;
21const costPerTRex = 5000;
22
23console.log("Fonction traditionnelle :");
24console.log(`Coût : $${calculateFeedingCost(trexCount, costPerTRex)}`);
25
26console.log("\nArrow function:");
27console.log(`Coût : $${calculateFeedingCostArrow(trexCount, costPerTRex)}`);
28
29console.log("\nArrow function (short):");
30console.log(`Coût : $${calculateFeedingCostShort(trexCount, costPerTRex)}`);
31
32// Arrow functions in array methods
33console.log("\n=== ARROW FUNCTIONS WITH ARRAYS ===\n");
34
35const dinosaurs = [
36 { name: 'T-Rex', danger: 10 },
37 { name: 'Velociraptor', danger: 8 },
38 { name: 'Triceratops', danger: 3 },
39 { name: 'Brachiosaurus', danger: 2 }
40];
41
42// Filter - dangerous uniquement
43const dangerous = dinosaurs.filter(dino => dino.danger > 5);
44console.log("Dinosaures dangereux :");
45dangerous.forEach(dino => console.log(` - ${dino.name} (danger: ${dino.danger}/10)`));
46
47// Map - liste des noms
48const names = dinosaurs.map(dino => dino.name);
49console.log("\nToutes les espèces :", names.join(', '));
50
51// Reduce - niveau de danger moyen
52const avgDanger = dinosaurs.reduce((sum, dino) => sum + dino.danger, 0) / dinosaurs.length;
53console.log(`\nMedium danger: ${avgDanger.toFixed(1)}/10`);Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Que fait la méthode .then() sur une Promise ?
2. À quoi sert la méthode .catch() sur une Promise ?
Tâches pratiques dans le jeu
- Éditeur de code
Réécris un callback hell en une chaîne de Promises - connecte trois opérations asynchrones avec .then() et ajoute la gestion d'erreurs avec .catch().
- Tri vertical
Ordonne les étapes de l'imbrication croissante des callbacks (callback hell) :