Un appel réseau met deux cents millisecondes à répondre. JavaScript, lui, ne sait pas attendre : il déroule ses instructions sans s'arrêter. Il a donc fallu un moyen d'annoncer ce qu'il faudra faire quand la réponse arrivera, sans figer la page.
Une promesse est cet objet intermédiaire : un résultat qui n'existe pas encore, et n'existera peut-être jamais.
Définition
Une Promise est un objet qui représente l'issue d'une opération asynchrone : une valeur livrée plus tard, ou un échec. On ne récupère pas cette valeur directement, on enregistre d'avance le traitement à lui appliquer.
const lireFacture = async (id) => {
if (id !== 42) throw new Error("Facture introuvable");
return { id, montant: 19 };
};
lireFacture(42)
.then((f) => console.log(f.montant)) // 19
.catch((err) => console.log(err.message))
.finally(() => console.log("terminé"));then reçoit la valeur en cas de succès, catch reçoit l'Error en cas d'échec, finally s'exécute dans les deux cas. Les trois renvoient une nouvelle promesse, d'où l'enchaînement.
Les trois états
| État | Ce qu'il signifie | Ce qui l'amène |
|---|---|---|
| En attente | Le résultat n'est pas connu | L'état de départ |
| Tenue | Une valeur est disponible | Un resolve ou un return |
| Rompue | Une erreur est disponible | Un reject ou un throw |
Le passage est définitif : le résultat ne bouge plus.
Une promesse démarre quand elle est créée, pas quand on l'attend : appeler une fonction asynchrone lance le travail tout de suite, même si le await arrive dix lignes plus bas.
await, la même chose en plus lisible
Dans une fonction async, await met la suite en pause jusqu'au règlement de la promesse, puis rend sa valeur. Les échecs redeviennent attrapables par un try.
async function afficherFacture(id) {
try {
const facture = await lireFacture(id);
console.log(facture.montant);
} catch (err) {
console.log("Indisponible :", err.message);
}
}Le code se lit de haut en bas alors qu'il ne s'exécute pas dans cet ordre, et le gain grandit avec chaque appel enchaîné.
Attendre plusieurs promesses
| Méthode | Ce qu'elle rend |
|---|---|
Promise.all | Toutes les valeurs, ou le premier échec |
Promise.allSettled | L'issue de chacune, sans jamais échouer |
Promise.race | La première réglée, succès ou échec |
Promise.any | La première tenue, les échecs étant ignorés |
async function toutCharger() {
const [profil, factures] = await Promise.all([
lireProfil(),
lireFactures(),
]);
console.log(profil, factures); // les deux appels ont couru ensemble
}Questions fréquentes
Faut-il encore écrire new Promise ?
Rarement. Toute fonction async renvoie déjà une promesse, et les interfaces modernes en produisent une d'elles-mêmes. Le constructeur ne sert plus qu'à emballer une interface à fonctions de rappel, comme setTimeout.
Que se passe-t-il si personne n'attrape un rejet ?
L'échec remonte au moteur sous forme de rejet non géré : dans un navigateur il finit dans la console, sous Node.js il arrête le processus depuis la version 15. Une promesse sans catch est un risque réel.
Enchaîner les await ou passer par Promise.all ?
Cela dépend des liens entre les appels. Si le second a besoin du résultat du premier, la séquence s'impose. Sinon, les lancer ensemble divise l'attente par leur nombre, un réflexe travaillé dans la formation JavaScript.