Apprenez en lisant dans l'ordre

Promise — recevoir un résultat asynchrone avec then et catch

Reçois plus tard un résultat avec Promise : ses trois états, then / catch / finally, le chaînage et Promise.all.

Certaines opérations, comme envoyer une commande ou vérifier un stock, mettent du temps à renvoyer un résultat. Pour que la page ne se fige pas pendant ce temps, on écrit ce genre de code de façon à passer tout de suite à la ligne suivante et à récupérer le résultat plus tard. Cette façon de faire s'appelle la programmation asynchrone.

Cet article couvre Promise, qui représente un résultat disponible plus tard, et Promise.all, qui attend plusieurs Promises à la fois.

Recevoir un résultat plus tard — Promise et then

La réponse à une demande de places disponibles arrive 100 ms après l'envoi. setTimeout (une fonction qui appelle une fonction une seule fois, après le nombre de millisecondes indiqué) rend la main immédiatement, et l'exécution passe à la ligne suivante : tu ne peux donc pas renvoyer avec return une réponse qui arrive 100 ms plus tard.

À la place, tu renvoies une Promise (un objet qui représente un résultat disponible plus tard), et la réponse est transmise par son intermédiaire. La fonction que tu passes à new Promise((resolve) => { ... }) est appelée immédiatement, et appeler resolve(valeur) à l'intérieur fait passer la Promise de pending (en attente d'un résultat) à fulfilled (réussie). La fonction passée à la méthode then est appelée avec cette valeur une fois la Promise fulfilled.

// Renvoie une Promise fulfilled avec value au bout de ms millisecondes
function delay(ms, value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

// Lancer la vérification (encore pending à ce stade)
const seats = delay(100, "Il reste 12 places");

// Passer la fonction à appeler une fois la Promise fulfilled
seats.then((message) => {
  console.log(message);                        // Il reste 12 places (affiché en 2e)
});

console.log("Vérification des places disponibles");  // Vérification des places disponibles (affiché en 1er)
La fonction passée à then s'exécute en dernier
Appel dedelay(100, …)Renvoie unePromise pendingFonction passéeà seats.then(…)Elle n'est pasencore appeléeconsole.log dela ligne suivanteMessage d'attenteaffiché en premier100 ms aprèsl'appel de delayfulfilled : lafonction s'exécute
then se contente de garder la fonction ; il ne l'appelle pas sur-le-champ. La fonction passée ne s'exécute qu'une fois les lignes suivantes terminées et la Promise fulfilled.

Si tu écris le code qui utilise le résultat en dehors de then, il s'exécute avant que le résultat soit disponible, et la valeur n'est donc pas encore disponible. Garde les lignes qui utilisent le résultat dans la fonction passée à then, et place ce que tu veux faire pendant l'attente sur les lignes qui suivent, en dehors de then.

Ajoute une ligne d'attente à la fin du code

L'environnement d'exécution de cette page ne récupère que ce qui est affiché avant la fin de la dernière ligne. Dans un navigateur ou dans Node.js, la fonction de then s'exécute même si rien ne l'attend, mais ici, tu ajoutes await delay(1000); à la fin pour attendre une seconde. await (une syntaxe qui ne passe pas à la ligne suivante tant que le résultat d'une Promise n'est pas connu) est présenté dans le prochain article.

Deux vidéos sont en cours de conversion : affiche une notification dès que chacune est terminée. delay est déjà déclaré.

① Dans convertVideo, renvoie une Promise fulfilled au bout de ms millisecondes avec un message de la forme « intro.mp4 : conversion terminée ».

② Convertis intro.mp4 en 150 ms et outro.mp4 en 80 ms, et affiche chaque message dès qu'il arrive.

③ Juste après ②, affiche « Tu peux continuer à éditer pendant la conversion ».

(Si tout s'exécute correctement, une explication apparaîtra.)

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Gérer l'échec — reject / catch / finally

La vérification du code PIN d'une carte échoue quand le code est faux. Si tu n'as que resolve, un échec arrive comme une valeur ordinaire, exactement comme un succès, et la fonction qui la reçoit a besoin d'un if à chaque fois pour les distinguer.

La fonction passée à new Promise peut aussi recevoir reject comme deuxième paramètre. L'appeler, comme dans reject(new Error("message")), rend la Promise rejected (échouée). La fonction passée à catch reçoit cette Error quand la Promise est rejected, et la fonction passée à finally est appelée en dernier, en cas de succès comme d'échec.

function verifyPin(pin) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (pin === "4821") {
        resolve("Code PIN vérifié");
      } else {
        // Signaler un échec en passant une Error à reject
        reject(new Error("Code PIN incorrect"));
      }
    }, 100);
  });
}

// then, catch et finally peuvent s'enchaîner les uns après les autres (la section suivante explique comment)
verifyPin("0000")
  .then((message) => console.log(message))        // pas appelée, puisque la Promise est rejected
  .catch((error) => console.log(error.message))   // Code PIN incorrect
  .finally(() => console.log("Champ de saisie vidé"));
  // Champ de saisie vidé (affiché en dernier, en cas de succès comme d'échec)
Comment resolve et reject décident de l'état
Créée avec newPromise (pending)Passer une valeurà resolvePasser une Errorà rejectN'appeler niresolve ni rejectDevientfulfilledDevientrejectedRestependingAppelle then,puis finallyAppelle catch,puis finallyNi then, ni catch,ni finally
Dans les deux colonnes de gauche, finally est appelée après then ou catch. Les fonctions appelées dépendent de l'appel à resolve ou à reject.

Quand une Promise est rejected, les fonctions then enchaînées avant catch sont sautées, et c'est la fonction du catch qui suit qui est appelée. Ce catch est une méthode de Promise, distincte de l'instruction try / catch. Si tu n'appelles jamais ni resolve ni reject, le code semble bloqué, sans affichage ni erreur.

Consulte le solde d'une carte cadeau. delay, balances et codes sont déjà déclarés.

① Dans checkGiftCard, si le code ne commence pas par « GIFT- », rejette la Promise tout de suite, sans attendre, avec une Error dont le message est « Format de code invalide ».

② Sinon, résous-la au bout de 100 ms avec un message de la forme « Solde : 3000 yens ».

③ Consulte chaque code de codes et affiche le message obtenu.

④ En cas de succès comme d'échec, affiche « Indicateur de recherche masqué » à la fin.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Exécuter des étapes dans l'ordre — enchaîner then et return

Après avoir passé une commande, tu veux envoyer un e-mail de confirmation avec le numéro de commande renvoyé. Si tu écris la deuxième requête dans le premier then, puis la troisième à l'intérieur de celle-ci, l'indentation se creuse à chaque étape et le code devient difficile à lire.

then renvoie une nouvelle Promise : tu peux donc enchaîner les appels, comme dans .then(...).then(...). C'est ce qu'on appelle le chaînage (accrocher le then suivant à la Promise renvoyée par le then précédent). Une valeur que tu renvoies avec return depuis une fonction de then est reçue par la fonction du then suivant.

// delay est le même que dans le code de la première section
const placeOrder = () => delay(100, "A-2046");
const sendMail = (orderId) => delay(100, `E-mail de confirmation envoyé pour ${orderId}`);

placeOrder()
  .then((orderId) => {
    console.log(`Numéro de commande : ${orderId}`);   // Numéro de commande : A-2046
    return sendMail(orderId);                        // la chaîne attend que l'e-mail soit envoyé
  })
  .then((result) => {
    console.log(result);                             // E-mail de confirmation envoyé pour A-2046
    return "Toutes les étapes sont terminées";       // la valeur passe directement à la fonction suivante
  })
  .then((status) => console.log(status));            // Toutes les étapes sont terminées
Sans return, then n'attend pas
returnsendMail(orderId)Attend l'envoide l'e-mailresult contientle résultatFin affichéeaprès l'envoiAppelle justesendMail(orderId)Le then suivantn'attend pasresult vautundefinedFin affichéeavant l'envoi
Seule la ligne du haut attend l'envoi de l'e-mail avant d'appeler la fonction suivante. Le then suivant n'attend le résultat que si tu renvoies la Promise avec return.

Sans return, la fonction renvoie undefined : le then suivant s'exécute donc sans attendre l'envoi de l'e-mail, et result vaut undefined. Aucune erreur n'apparaît ; le résultat de l'envoi est simplement perdu, ce qui rend cet oubli facile à rater.

Dès que tu ajoutes des accolades, tu ne peux plus omettre return

Une fonction fléchée sans accolades, comme .then((id) => sendMail(id)), renvoie telle quelle la Promise de sendMail : le then suivant attend donc. Si tu entoures le corps d'accolades pour ajouter une ligne de journal au milieu, remets le return, comme dans return sendMail(id);.

Paie en yens un article à 12 dollars acheté dans une boutique à l'étranger. delay, dollars, loadRate et chargeCard sont déjà déclarés.

① Utilise le taux pour convertir dollars en yens, affiche « Montant converti : 1800 yens » et transmets le montant.

② Débite ce montant, et ne passe pas à la suite tant que le résultat n'est pas connu.

③ Affiche le résultat du paiement.

④ Si quelque chose échoue en cours de route, affiche « Paiement impossible : » suivi du message de l'Error.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Attendre plusieurs résultats à la fois — Promise.all

À l'ouverture du tableau de bord d'administration, tu demandes séparément le chiffre d'affaires du jour, le nombre de commandes et le nombre de demandes de contact, et tu n'affiches le panneau récapitulatif qu'une fois les trois résultats arrivés. Si tu accroches un then distinct à chacune des trois Promises, celle qui arrive en dernier change à chaque fois : il n'y a donc aucun endroit unique où vérifier que tout est arrivé.

Passe à Promise.all (une méthode qui prend un tableau de Promises et attend que tous leurs résultats soient arrivés) les Promises renvoyées par les trois fonctions : tu reçois les trois résultats d'un coup, sous forme de tableau. Si une seule d'entre elles est rejected, c'est catch qui prend le relais au lieu de then.

// delay est le même que dans le code de la première section
const loadSales = () => delay(100, 184000);
const loadOrders = () => delay(50, 42);
const loadInquiries = () => delay(80, 3);

// Les trois démarrent dès l'appel des fonctions. Promise.all attend qu'elles soient toutes arrivées
Promise.all([loadSales(), loadOrders(), loadInquiries()])
  // Recevoir le tableau de résultats en le déstructurant par position
  .then(([sales, orders, inquiries]) => {
    // Reçus dans l'ordre écrit dans le tableau, pas dans l'ordre où ils se terminent
    console.log(`Ventes : ${sales} yens`);       // Ventes : 184000 yens
    console.log(`Commandes : ${orders}`);        // Commandes : 42
    console.log(`Demandes : ${inquiries}`);      // Demandes : 3
  })
  .catch((error) => console.log(error.message));
L'ordre de passage n'est pas l'ordre d'arrivée
Index 0loadSales()184000 à 100 ms3e à finirIndex 1loadOrders()42 à 50 ms1re à finirIndex 2loadInquiries()3 à 80 ms2e à finir
Les trois se terminent à 50 ms, 80 ms et 100 ms, dans cet ordre. then reçoit [184000, 42, 3], dans l'ordre où elles ont été passées (colonne de gauche).

Promise.all ne lance lui-même aucune requête ; il se contente d'attendre que toutes soient arrivées. Le tableau ci-dessous montre, pour chaque état des Promises passées, la Promise renvoyée par Promise.all et la fonction qui est appelée.

État des Promises passéesPromise renvoyée par Promise.allFonction appelée et valeur reçue
Toutes fulfilledFulfilled dès que la dernière est fulfilledthen reçoit un tableau dans l'ordre de passage
Une est rejectedRejected dès que celle-ci est rejectedcatch reçoit cette Error
Deux ou plus sont rejectedRejected dès que la première est rejectedcatch ne reçoit que la première Error
Les autres sont fulfilled, mais une reste pendingReste pendingNi then ni catch n'est appelé

Obtiens un devis global pour un voyage. Les quatre fonctions dont le nom commence par quote sont déjà déclarées.

① Dans estimateTrip, attends les résultats de toutes les Promises reçues.

② Affiche le total des prix sous la forme « Total : 68500 yens ».

③ Si une seule échoue, affiche « Devis impossible : » suivi du message de l'Error.

④ Demande un devis une première fois avec le vol, l'hôtel et la voiture de location, puis une seconde fois en remplaçant la voiture par quoteFullCar, pour laquelle tout est réservé.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat
QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Si delay(100, "A").then((v) => console.log(v)); est suivi de console.log("B");, dans quel ordre s'affiche la sortie ?

Question 2Si tu enchaînes .then(A).catch(B).finally(C) sur une Promise qui est rejected, lesquelles sont appelées ?

Question 3Quel tableau reçoit le then de Promise.all([delay(100, "A"), delay(50, "B")]) ?