Est-ce que JavaScript Promise.all a un rappel qui est déclenché lorsqu'il y a des échecs réussis ET

Suis-je mal compris Promise.all? J'ai des promesses X dans un tableau et j'essaie d'agréger le rapport succès / échec du tableau.

Voici ce que je pense savoir:

Promise.tout prend une série de promesses.

Si toutes les promesses réussissent, alors le rappel est exécuté.

Si l'une des promesses échoue alors le rappel de .catch est appelé et l'argument passé est la valeur de l'unique erreur soulevée.

Il n'y a pas de rappel lancé qui est le résultat de toutes les promesses si certains réussissent et certains échouent. C'est-à-dire qu'il ne peut pas vous donner un tableau comme (pseudo-code) [success, fail, success, success] – comme on s'y attendait et on peut trouver dans de nombreuses bibliothèques JS (ajax, ember, etc.).

C'est comme .then C'est plus comme un .success , pas une fonction qui fonctionne toujours après que toutes les promesses ont été réalisées indépendamment du fait que certains a réussi ou que certains ont échoué. Pourquoi n'a-t-il pas un .finally .runThisShizNoMatterWhat .finally .runThisShizNoMatterWhat ?? Ou me manque-t-il quelque chose (très probable)?

Ceci est lié à Bluebird Promise.all – plusieurs promesses ont complété le succès global et les rejets , mais c'est Bluebird-spécifique. Le cœur de la question est que si vous souhaitez vérifier si quelque chose a réussi ou échoué, vous ne demandez pas vraiment le résultat direct de chaque promesse. Au lieu de cela, vous voudriez transformer les promesses avant d'utiliser Promise.all . Il n'y a pas d'aide pour ces promesses standard de ES6, mais il est trivial de mettre en œuvre. Dans la plupart des bibliothèques, cela s'appelle Promise.settle . Par exemple

 var someThings = [...]; // some list of promises that may succeed or fail settle(someThings).then(results => { results.forEach(result => { if (result.state === 'fullfilled'){ console.log('succeeded', result.value); } else { console.log('failed', result.value); } }); }); function settle(arr){ return Promise.all(arr.map(promise => { return promise.then( value => ({state: 'fullfilled', value}), value => ({state: 'rejected', value}) ); })); } 

Si l'une des promesses rejette, alors la promesse retournée par Promise.all est rejetée. Donc, le gestionnaire de rejet sera appelé juste après l'un des promesses de rejet. Ce n'est peut-être pas le comportement souhaité si vous voulez simplement exécuter toutes les Promesses sans vous soucier des rejets (c.-à-d. Ne pas rejeter la promesse si une promesse rejette).

Vous pouvez toujours gérer chaque rejet de promesse individuelle afin qu'il remplisse après le rejet.

 var promiseRejected = new Promise(function(resolve, reject){ setTimeout(function(){ reject('I was rejected'); }, 1000); }); promiseRejected = promiseRejected.then(null, function(reason){ //I was rejected, now lets fullfill it. return reason; }); var promiseResolved = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('All good'); }, 1500); }); var time = performance.now(); Promise.all([promiseRejected, promiseResolved]).then(function(results){ //both promises fulfilled; 1500 msecs passed console.log(results[0], results[1], performance.now() - time); }); 

Si vous dites bien des erreurs en dehors de vos valeurs, alors faire ce que vous voulez est aussi simple que:

 Promise.all(array.map(promise => promise.catch(error => error))) 
 var log = msg => div.innerHTML += "<p>" + msg + "</p>"; var a = () => Promise.resolve(1); var b = () => Promise.reject("error"); var c = () => Promise.resolve(3); Promise.all([a(), b(), c()].map(p => p.catch(e => e))).then(r => log(r)); 
 <div id="div"></div> 

Promise.all crée une nouvelle promesse qui ne peut être résolue ou rejetée que dans son ensemble. Peut-être que vous pouvez penser à ce que la méthode de every méthode de tableau soit renvoyée avec faux lorsque le premier élément ne correspond pas au prédicat.

La fonction then prend jusqu'à deux arguments, le second étant le gestionnaire rejeté. Dans ce sens, c'est plus qu'un simple success , il peut effectivement gérer tous les cas. La catch n'est qu'une méthode de commodité, courte pour .then(undefined, function(reason) { ... }) .

L'API prometteur n'a pas ce dont vous avez besoin J'ai peur, vous devriez l'implémenter vous-même.

De votre question, il semble que vous vous attendez à régler toutes les promesses, ce qui promet de la méthode. promise.all ne garantit pas, la seule méthode promise.settle garanties pour régler chaque promesse dans le tableau.

Si vous voulez le résultat de la promise.all . Tout en réglant chaque promesse, plus une notification dont la promesse a été résolue ou rejetée, la méthode spex.batch est exactement ce dont vous avez besoin.

Exemple copié à partir du traitement par lots :

 var spex = require('spex')(Promise); // function that returns a promise; function getWord() { return Promise.resolve("World"); } // function that returns a value; function getExcl() { return '!'; } // function that returns another function; function nested() { return getExcl; } var values = [ 123, "Hello", getWord, Promise.resolve(nested) ]; spex.batch(values) .then(function (data) { console.log("DATA:", data); }, function (reason) { console.log("REASON:", reason); }); 

Cette sortie:

 DATA: [ 123, 'Hello', 'World', '!' ] 

Maintenant, faisons échouer en changeant getWord à ceci:

 function getWord() { return Promise.reject("World"); } 

Maintenant, la sortie est:

 REASON: [ { success: true, result: 123 }, { success: true, result: 'Hello' }, { success: false, result: 'World' }, { success: true, result: '!' } ] 

C'est-à-dire que l'ensemble du tableau est réglé, en signalant les résultats liés à l'index.

Et si, au lieu de signaler toute la raison, nous appelons getErrors() :

 console.log("REASON:", reason.getErrors()); 

Alors la sortie sera:

 REASON: [ 'World' ] 

Ceci est juste pour simplifier l'accès rapide à la liste des erreurs survenues.

Et vous pouvez voir à partir du protocole de la méthode que vous pouvez passer en option cb – callback paramètre qui vous indiquera laquelle des promesses résolues et celles rejetées.

Je suis d'accord que l'utilisation de Bluebird.reflect pour mettre en place le règlement est la meilleure façon, si vous utilisez Bluebird. Voici une autre solution qui peut être bonne en fonction de votre cas d'utilisation. Cela a fonctionné pour moi dans une situation quelque peu intéressante. Cela suppose également Bluebird comme la bibliothèque prometteuse.

Dans mon cas, j'ai un ensemble de fonctions ("tâches") qui sont toutes enveloppées dans Promise.method. Ces tâches peuvent renvoyer une promesse rejetée ou ils peuvent renvoyer ou jeter des valeurs simples qui deviendront des résolutions prometteuses. Je devais exécuter tous ces éléments et collecter tous les résultats (pas seulement la première défaillance).

Encore une fois, gardez à l'esprit que chaque élément dans le tableau des tâches est une fonction Promise.method wrapped. (Facile à mettre en œuvre. Voir http://bluebirdjs.com/docs/api/promise.method.html )

 var runTasks = function(tasks) { return Bluebird.reduce(tasks, function (results, task) { return task() .then(function (result) { results.success.push(result) return results }) .caught(function (result) { results.fail.push(result) return results }) }, { success: [], fail: [] }) } 

Vous l'appelez ainsi, récupérer un objet qui a un ensemble de valeurs remplies et un tableau de valeurs rejetées:

 runTasks(taskArray) .then(function(results){ // do whatever you want here // results.success is an array of the returned/resolved values // results.fail is an array of the rejected/thrown values })