Promise.race() en JavaScript : garder la première promesse réglée

Promise.race() rend l'issue de la première promesse réglée, succès ou échec : poser un délai maximum, la différence avec any, et ce qu'elle n'annule pas.
3 min de lecture
Believemy logo

Une requête qui met trente secondes à répondre n'est pas une requête lente, c'est une requête perdue. Encore faut-il pouvoir décider soi-même du moment où l'on renonce.

Promise.race sert précisément à cela : mettre deux issues en concurrence et retenir celle qui se présente la première.


Définition

Promise.race(liste) renvoie une promesse qui prend l'issue de la première promesse réglée de la liste, qu'elle soit tenue ou rompue. Les autres continuent de tourner, leur résultat est ignoré.

JAVASCRIPT
const differe = (v, ms) => new Promise((r) => setTimeout(() => r(v), ms));

async function demo() {
  const gagnant = await Promise.race([
    differe("lent", 300),
    differe("rapide", 50),
  ]);
  console.log(gagnant);   // rapide
}

demo();

Le mot course est bien choisi : il n'y a qu'un vainqueur, et une promesse rompue arrivée avant les autres gagne au même titre qu'une promesse tenue.


Poser un délai maximum

C'est l'usage principal. On fait courir le travail réel contre une promesse qui échoue après un temps donné, et le premier des deux tranche.

JAVASCRIPT
const echoueApres = (ms) => new Promise((_, rejeter) => {
  setTimeout(() => rejeter(new Error("Délai dépassé")), ms);
});

async function avecDelai(travail, ms) {
  return Promise.race([travail, echoueApres(ms)]);
}

avecDelai(differe("réponse", 400), 100)
  .then((v) => console.log(v))
  .catch((err) => console.log(err.message));   // Délai dépassé

L'appelant récupère une erreur claire au lieu d'une attente sans fin, et le catch traite le dépassement comme n'importe quel autre échec.


race, any : deux courses différentes

MéthodeCe qui déclenche le résultatEn cas d'échecs répétés
Promise.raceLa première réglée, succès ou échecLe premier échec l'emporte
Promise.anyLa première tenue seulementUn AggregateError une fois toutes rompues

Ces trois colonnes distinguent deux besoins. race répond à la question du temps écoulé, any à celle du premier serveur qui répond correctement parmi plusieurs miroirs.

Bon à savoir

Promise.race([]) renvoie une promesse qui ne se règle jamais, puisqu'aucun concurrent ne peut gagner. Un await posé dessus fige la suite de la fonction sans lever la moindre erreur : vérifiez que la liste n'est pas vide avant de la passer.


Questions fréquentes

Question

Le perdant est-il annulé ?

Non, et c'est le malentendu le plus coûteux. La promesse écartée poursuit son travail jusqu'au bout : la requête part quand même, le serveur répond quand même, la facture est la même. Pour interrompre réellement l'appel, il faut un AbortController.


Question

Que devient le rejet du perdant ?

Il survient après coup, sans personne pour l'écouter, et le moteur le signale comme rejet non géré. Un catch vide branché sur la promesse écartée suffit à le neutraliser, ou mieux, une annulation propre qui l'empêche de se produire.


Question

Comment réessayer plutôt que d'abandonner ?

En enveloppant l'appel dans une boucle qui relance après un échec, avec une pause croissante entre les tentatives. La course fixe la limite d'une tentative, la boucle décide du nombre d'essais : les deux mécanismes se combinent et n'ont pas à être confondus.

Termes connexes

Découvrez notre glossaire JavaScript

Tous les mots de JavaScript expliqués simplement : mots-clés, objets natifs, méthodes, erreurs et concepts. Définitions claires et exemples qui tournent, pour apprendre et pour se dépanner.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.