Bonjour ! Bienvenue dans cette nouvelle leçon sur les fondamentaux de Node.js.
Introduction
Dans la leçon précédente, nous avons découvert le cœur de Node.js : son modèle d'exécution asynchrone non-bloquant et la boucle d'événements (Event Loop). Nous avons compris pourquoi et comment Node.js gère efficacement des milliers d'opérations simultanées avec un seul thread.
Aujourd'hui, nous passons de la théorie à la pratique. Nous allons apprendre à maîtriser l'outil principal pour gérer ces opérations asynchrones : les Promises (Promesses). Pour vous qui venez de l'environnement Laravel/PHP, où le code s'exécute souvent de manière séquentielle, les Promises représentent un concept fondamental pour écrire du code Node.js moderne et robuste. Elles sont la solution élégante au "callback hell" (l'enfer des callbacks), un problème qui rendait le code asynchrone difficile à lire et à maintenir.
À la fin de cette leçon, vous serez capable d'utiliser les Promises pour enchaîner des opérations asynchrones de manière claire, gérer les succès comme les erreurs, et exécuter plusieurs tâches en parallèle.
1. Qu'est-ce qu'une Promise ?
Une Promise est un objet JavaScript qui sert de placeholder (substitut) pour une valeur qui n'est pas encore disponible. C'est la promesse qu'une opération asynchrone se terminera un jour et vous donnera un résultat (soit une valeur de succès, soit une erreur).
Pensez à une commande dans un restaurant :
- Vous passez commande et recevez un ticket. Ce ticket est votre Promise.
- Pendant que la cuisine prépare votre plat, votre Promise est à l'état
pending(en attente). - Finalement, deux issues sont possibles :
- Votre plat arrive : la Promise est
fulfilled(tenue) avec le plat comme valeur. - La cuisine est en rupture de stock : la Promise est
rejected(rejetée) avec une explication (l'erreur).
- Votre plat arrive : la Promise est
Une fois la Promise "tenue" ou "rejetée", son état est définitif. On dit qu'elle est settled (réglée).

2. Créer et consommer une Promise
Pour bien comprendre le cycle de vie d'une Promise, il est essentiel de voir comment on la crée et comment on l'utilise.
La vidéo suivante, du cours de Grafikart.fr, est une excellente introduction pratique. Elle vous montrera comment créer une Promise, comment la résoudre ou la rejeter, et comment réagir à ces issues.
Apprendre le JavaScript : Promise
Cette vidéo vous guidera à travers la création et la consommation de Promises. Vous y découvrirez la syntaxe de base et les méthodes essentielles.
Regardez la vidéo du début jusqu'à 07:23. Concentrez-vous sur les points suivants : La syntaxe new Promise((resolve, reject) => { ... }). Le rôle des fonctions resolve et reject. L'utilisation des méthodes .then() pour le succès, .catch() pour l'échec, et .finally() pour le nettoyage.
Synthèse des concepts clés
Comme vous l'avez vu, il y a deux côtés à une Promise : le créateur (la fonction qui retourne la Promise) et le consommateur (le code qui l'utilise).
Créer une Promise :
On utilise le constructeur new Promise. Il prend une fonction (appelée "executor") avec deux arguments : resolve et reject.
- Appelez
resolve(valeur)lorsque l'opération réussit. - Appelez
reject(erreur)lorsque l'opération échoue.
function fetchUserData() {
return new Promise((resolve, reject) => {
// Simule un appel réseau qui prend 1 seconde
setTimeout(() => {
const success = true; // Changez à 'false' pour tester l'échec
if (success) {
resolve({ id: 1, name: "John Doe" });
} else {
reject(new Error("Impossible de récupérer les données utilisateur."));
}
}, 1000);
});
}
Consommer une Promise :
Une fois que vous avez une Promise, vous utilisez des méthodes pour "attendre" son résultat.
.then(onFulfilled, onRejected): La méthode principale.onFulfilledest appelée si la Promise estfulfilled. Elle reçoit la valeur passée àresolve.onRejectedest appelée si la Promise estrejected. Elle reçoit l'erreur passée àreject.
.catch(onRejected): C'est un raccourci syntaxique pour.then(null, onRejected). C'est la manière la plus courante et la plus lisible de gérer les erreurs..finally(onFinally): La fonctiononFinallyest exécutée que la Promise soit tenue ou rejetée. Idéal pour le code de nettoyage (ex: cacher un indicateur de chargement).
const userPromise = fetchUserData();
userPromise
.then(user => {
console.log("Succès :", user.name);
})
.catch(error => {
console.error("Erreur :", error.message);
})
.finally(() => {
console.log("Opération terminée.");
});
3. La puissance du chaînage (Chaining)
Le véritable avantage des Promises par rapport aux callbacks est leur capacité à être chaînées.
Chaque appel à .then(), .catch() ou .finally() retourne une nouvelle Promise. Cela permet de créer des séquences d'opérations asynchrones lisibles et plates, évitant ainsi le "pyramide de la mort" (pyramid of doom).
Promises - 33 JavaScript Concepts
L'article 'Promises - 33 JavaScript Concepts' contient une excellente section qui illustre le chaînage et la règle la plus importante à respecter.
Lisez la section intitulée 'Promise Chaining'. Portez une attention particulière à la sous-section 'The #1 Rule of Chaining: Always return from your .then() callbacks!'. C'est l'erreur la plus commune chez les débutants.
Comment fonctionne le chaînage ?
La valeur que vous retournez dans un .then() devient la valeur de résolution de la Promise suivante dans la chaîne.
Promise.resolve(2) // Crée une Promise déjà résolue avec la valeur 2
.then(value => {
console.log(value); // 2
return value * 2; // retourne 4
})
.then(value => {
console.log(value); // 4
// Si on retourne une nouvelle Promise, la chaîne attend qu'elle se résolve
return new Promise(resolve => setTimeout(() => resolve(value + 10), 500));
})
.then(value => {
console.log(value); // 14 (après 500ms d'attente)
});
La règle d'or : TOUJOURS return dans un .then() si vous voulez passer une valeur (ou une autre Promise) à l'étape suivante. Si vous oubliez, l'étape suivante recevra undefined.
4. Gérer plusieurs Promises simultanément
Que faire si vous devez lancer plusieurs opérations asynchrones en parallèle et attendre qu'elles soient toutes terminées ? C'est un cas très courant (ex: charger les informations d'un utilisateur et ses posts en même temps). Pour cela, la classe Promise fournit des méthodes statiques très utiles.
La suite de la vidéo de Grafikart explique parfaitement ces méthodes.
Apprendre le JavaScript : Promise
Cette partie de la vidéo aborde les méthodes statiques pour combiner des Promises, comme Promise.all.
Regardez la vidéo de 15:40 à 20:54. Concentrez-vous sur la différence entre Promise.all et Promise.allSettled, les deux méthodes les plus utiles au quotidien.
Voici un tableau récapitulatif basé sur la documentation :
| Méthode | Se résout quand... | Rejette quand... | Cas d'usage principal |
|---|---|---|---|
Promise.all() |
TOUTES les Promises se résolvent. | UNE SEULE Promise rejette (échoue immédiatement). | Vous avez besoin de tous les résultats pour continuer. C'est un "tout ou rien". |
Promise.allSettled() |
TOUTES les Promises sont réglées (résolues ou rejetées). | Jamais. | Vous voulez tenter toutes les opérations et savoir lesquelles ont réussi ou échoué. |
Promise.race() |
La première Promise qui se règle (résout ou rejette). | La première Promise qui se règle rejette. | Implémenter un timeout pour une opération. |
Promise.any() |
La première Promise qui se résout. | TOUTES les Promises rejettent. | Tenter plusieurs sources pour une même ressource et prendre la première qui répond. |
Exemple avec Promise.all:
const promiseUser = fetchUserData();
const promisePosts = fetchUserPosts(); // Supposons que cette fonction existe
Promise.all([promiseUser, promisePosts])
.then(([user, posts]) => {
// Ce bloc s'exécute seulement si les DEUX promises réussissent
console.log(`Utilisateur: ${user.name}`);
console.log(`Nombre de posts: ${posts.length}`);
})
.catch(error => {
// Ce bloc s'exécute si L'UNE des promises échoue
console.error("Une des opérations a échoué:", error);
});
5. Lien avec la Boucle d'Événements
Rappelez-vous de la leçon précédente : nous avons distingué la Task Queue de la Microtask Queue. C'est ici que cette connaissance devient cruciale.
Les callbacks des Promises (.then, .catch, .finally) sont ajoutés à la Microtask Queue. Cette file a une priorité plus élevée : elle est entièrement vidée après chaque tâche de la Task Queue.
Regardons un dernier extrait vidéo pour solidifier ce concept.
Apprendre le JavaScript : Promise
Cet extrait final montre clairement comment le code d'une Promise interagit avec l'Event Loop, en confirmant que les callbacks .then sont asynchrones et passent par la file d'attente.
Regardez la vidéo de 20:59 à 24:01. L'exemple illustre parfaitement pourquoi un .then() ne s'exécute pas immédiatement, même si la Promise est déjà résolue. C'est la boucle d'événements en action.
L'ordre d'exécution est donc :
- Tout le code synchrone du script principal.
- Toutes les tâches dans la Microtask Queue (callbacks de Promises).
- Une tâche de la Task Queue (ex: un callback de
setTimeout).
console.log("1. Début du script");
setTimeout(() => {
console.log("4. setTimeout (Task Queue)");
}, 0);
Promise.resolve().then(() => {
console.log("3. Promise.then (Microtask Queue)");
});
console.log("2. Fin du script");
// Ordre d'affichage dans la console :
// 1. Début du script
// 2. Fin du script
// 3. Promise.then (Microtask Queue)
// 4. setTimeout (Task Queue)
Conclusion
Aujourd'hui, vous avez appris à manier les Promises, la pierre angulaire de la programmation asynchrone en JavaScript moderne.
Points clés à retenir :
- Une Promise est un objet représentant le résultat futur d'une opération asynchrone, avec trois états :
pending,fulfilled, etrejected. - On consomme une Promise avec
.then()pour le succès,.catch()pour l'échec, et.finally()pour le nettoyage. - Le chaînage est la force des Promises, permettant d'écrire du code asynchrone plat et lisible. N'oubliez jamais de
returnla valeur ou la Promise suivante. Promise.all()etPromise.allSettled()sont des outils puissants pour gérer des opérations concurrentes.- Les callbacks de Promises utilisent la Microtask Queue, ce qui leur donne une haute priorité d'exécution dans la boucle d'événements.
Dans la prochaine leçon, nous allons découvrir la syntaxe async/await. C'est une manière encore plus élégante et intuitive de travailler avec les Promises, qui vous permettra d'écrire du code asynchrone qui ressemble à du code synchrone.