Advanced Typescript
Javascript/Promises

Parallèles

L'objet Promise dispose de plusieurs méthodes statiques très utiles pour orchestrer plusieurs opérations asynchrones à la fois.

Promise.all()

Imaginez que vous commandiez un kebab, des frites et une boisson. Vous voulez recevoir votre plateau uniquement lorsque tous les éléments sont prêts.
C'est exactement ce que fait Promise.all().

Promise.all(promises) prend un tableau de promesses et retourne une nouvelle promesse qui :

  • Se résout quand toutes les promesses du tableau sont résolues. La valeur est un tableau des résultats.
  • Rejette dès que la première des promesses du tableau est rejetée. C'est tout ou rien.
const cookKebab = new Promise(resolve => setTimeout(() => resolve('Kebab'), 2000));
const cookFries = new Promise(resolve => setTimeout(() => resolve('Frites'), 1000));
const prepareDrink = new Promise(resolve => setTimeout(() => resolve('Coca'), 500));

Promise.all([cookKebab, cookFries, prepareDrink])
  .then(function(plateau) {
    console.log(`Votre plateau est prêt : ${plateau.join(', ')}`); 
    // S'affiche après 2 secondes (le temps de la promesse la plus longue)
    // Résultat: "Votre plateau est prêt : Kebab, Frites, Coca"
  });

Promise.race()

Imaginez maintenant que vous ayez deux friteuses pour préparer des frites.
L'une a de l'huile déjà chaude, tandis que l'autre doit encore chauffer. Vous lancez les deux en même temps. Laquelle servira la première portion ?
C'est le principe de Promise.race() : seul le premier arrivé compte.

Elle prend un tableau de promesses et retourne une nouvelle promesse qui se résout ou se rejette dès que la première des promesses du tableau se résout ou se rejette.

const friteuseHuileChaude = new Promise(resolve => {
  setTimeout(() => resolve('🍟 Frites (huile chaude)'), 1000); // Rapide !
});

const friteuseHuileFroide = new Promise(resolve => {
  setTimeout(() => resolve('🍟 Frites (huile froide)'), 4000); // Lent...
});

console.log("C'est la course des friteuses !");

Promise.race([friteuseHuileChaude, friteuseHuileFroide])
  .then(function(premierePortion) {
    console.log(`La première portion est prête : ${premierePortion}`);
    // S'affiche après 1 seconde seulement, sans attendre l'autre friteuse.
  });

Promise.allSettled()

Parfois, on veut lancer plusieurs opérations indépendantes et savoir lesquelles ont réussi ou échoué, sans que tout s'arrête à la première erreur.

Promise.allSettled(promises) attend que toutes les promesses soient terminées (résolues ou rejetées) et retourne une promesse qui se résout toujours avec un tableau d'objets décrivant le statut de chaque promesse.

Chaque objet de statut contient :

  • status: 'fulfilled' et une value si la promesse a réussi.
  • status: 'rejected' et une reason (l'erreur) si elle a échoué.
const successfulPromise = Promise.resolve('Succès !');
const failedPromise = Promise.reject(new Error('Échec !'));
const pendingPromise = new Promise(resolve => setTimeout(() => resolve('Fini !'), 1000));

Promise.allSettled([successfulPromise, failedPromise, pendingPromise])
  .then(function(results) {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log(`Réussi avec la valeur : ${result.value}`);
      } else {
        console.error(`Échoué avec l'erreur : ${result.reason.message}`);
      }
    });
  });

/* Output :
Réussi avec la valeur : Succès !
Échoué avec l'erreur : Échec !
(après 1s) Réussi avec la valeur : Fini !
*/

À vous de jouer

Context

Vous travaillez pour une agence spatiale. Une sonde sur Mars doit vous envoyer des données provenant de différents instruments. Cependant, les téléchargements peuvent prendre du temps et parfois échouer à cause des conditions difficiles.

Pour simuler cela, nous utiliserons la fonction downloadData ci-dessous. Elle retourne une promesse qui simule un téléchargement.