Eviter le « fail-fast » de Promise.All()

Avec l’arrivée d’ES6 il est maintenant possible d’exécuter en parallèle plusieurs tâches très facilement sans avoir à utiliser de composant tierce comme Q. L’avantage de cette méthode est de pouvoir optimiser les temps de traitement sur une page. Attention cependant à ne pas en abuser ou vous risqueriez de compromettre votre navigateur. C’est intéressant si vous avez une dizaine d’actions à effectuer en même temps, mais si vous commencez à traiter un millier de demande, je ne donne pas cher de votre navigateur qui « freezera » certainement. Mieux vaut dans ce cas chaîner les actions afin d’éviter ça, même si cela doit engendrer des traitements plus long.

Dans le cas qui nous concerne, ES6 propose la méthode Promise.All(). Cette méthode retourne une promesse quand l’ensemble des promesses passées en argument sont résolues, ou une erreur quand l’une des promesse échoue. On parle alors de fail-fast ou échec rapide. Cette dernière action peut s’avérer assez handicapante si par exemple vous êtes en train de charger des référentiels en arrière plan et que l’un d’eux ne réponds pas. Vous n’avez pas forcément envie que cela arrête l’ensemble de vos traitements.

Voilà un exemple d’utilisation de Promise.All() :

var p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'Tâche 1');
});
var p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'Tâche 2');
});
var p3 = new Promise((resolve, reject) => {
  reject('Erreur');
});
 
Promise.all([p1, p2, p3]).then(values => {
  console.log(values);
}, error => {
  console.log(error)
});
 
// Dans la console : "Erreur"

On remarque ici le rejet instantanée de nos promesses au profit de l’erreur. Il existe bien entendu une manière de contourner ce comportement. Vous pouvez intercepter l’erreur de chacune de vos promesses et les gérer de cette manière :

var p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'Tâche 1');
});
 
var p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'Tâche 2');
});
 
var p3 = new Promise((resolve, reject) => {
  reject(new Error('Erreur'));
});
 
Promise.all([
  p1.catch(error => { return error }),
  p2.catch(error => { return error }),
  p3.catch(error => { return error })
]).then(values => {
  console.log(values[0]); // "Tâche 1"
  console.log(values[1]); // "Tâche 2"
  console.log(values[2]); // "Erreur"
});

Il existe heureusement une façon plus élégante pour écrire la manière de gérer le « fail-fast » :

var p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'Tâche 1');
});
 
var p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'Tâche 2');
});
 
var p3 = new Promise((resolve, reject) => {
  reject(new Error('Erreur'));
});
 
Promise.all([p1, p2, p3].map(p=> {
  p.then(value => {
    console.log(value);
  }).catch(error => {
    console.log(error);
  });
}));

Ressources

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Eviter le « fail-fast » de Promise.All()

par Cyrille Perrot Temps de lecture: 2 min
0