JavaScript Promise.all是否有成功和失败时触发的回调

时间:2015-10-06 20:12:47

标签: javascript ecmascript-6 es6-promise

我误解了Promise.all吗?我在数组中有X个promise,我试图聚合数组的成功/失败率。

以下是我认为我知道的事情:

Promise.all采取一系列承诺。

如果所有承诺都成功,则运行.then回调。

如果其中一个promise失败,则调用.catch回调,传入的参数是单个引发错误的值。

如果有些成功并且有些失败,则没有触发回调,这是所有承诺的结果。即它不能给你一个类似(伪代码)[success, fail, success, success]的数组 - 就像人们所期望的那样,可以在许多JS库中找到它们(ajax,ember等)。

就像.then更像是.success,而不是总是在完成所有承诺后运行的功能,无论某些成功还是失败。< / strong>为什么没有.when .finally .runThisShizNoMatterWhat ??或者我错过了什么(非常可能)?

6 个答案:

答案 0 :(得分:13)

这与Bluebird Promise.all - multiple promises completed aggregating success and rejections有关,但与蓝鸟有关。问题的核心是,如果你想检查某些事情是成功还是失败,那么你并不是真的要求每个承诺的直接结果。相反,您希望在使用Promise.all之前转换承诺。这个ES6标准的承诺没有帮助,但实现起来却微不足道。在大多数库中,这称为Promise.settle。例如

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})
    );
  }));
}

答案 1 :(得分:3)

如果其中一个承诺拒绝,则Promise.all返回的承诺将被拒绝。所以拒绝处理程序将在其中一个承诺拒绝后立即被调用。如果您只是想在不担心拒绝的情况下运行所有​​承诺(即,如果任何承诺拒绝,请不要拒绝承诺),这可能不是理想的行为。

您仍然可以处理每个承诺拒绝,以便在拒绝后履行承诺。

&#13;
&#13;
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);
  });
&#13;
&#13;
&#13;

一个promise构造函数,在所有promise都被解析/拒绝时解析,例如:

Promise.when = function (arrPromises) {
    if (!Array.isArray(arrPromises)) {
        return new TypeError('Expecting an Array of Promises');
    }
    return new Promise(function (resolve, reject) {
        var len = arrPromises.length,
            values = [],
            settled = 0;

        function settle(value, index) {
            values[index] = value;
            settled++;
            if (len === settled) {
                resolve(values);
            }
        }
        if (len === 0) {
            resolve([]);
        } else {
            arrPromises.forEach(function (promise, index) {
                var handler = function (value) {
                    settle(value, index);
                };
                promise.then(handler, handler);
            });
        }
    });
}

答案 2 :(得分:3)

如果除了你的价值观之外你还能说错误,那么做你想做的事就像这样简单:

Promise.all(array.map(promise => promise.catch(error => error)))

&#13;
&#13;
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));
&#13;
<div id="div"></div>
&#13;
&#13;
&#13;

答案 3 :(得分:2)

Promise.all创建了一个新的承诺,只能作为一个整体解决或拒绝。也许你可以认为它具有every数组方法语义,当第一个元素与谓词不匹配时,它返回false。

then函数最多需要两个参数,第二个参数是被拒绝的处理程序。从这个意义上讲,它不仅仅是success,它实际上可以处理所有情况。 catch只是一种便捷方法,是.then(undefined, function(reason) { ... })的缩写。

promise API没有您需要的东西我担心,您必须自己实现它。

答案 4 :(得分:1)

从您的问题看来,您似乎希望解决所有承诺,promise.all方法无法保证,只有方法promise.settle才能保证解决数组中的每个承诺。

如果您想要promise.all的结果,同时还要解决每个承诺,并且通知已解决或拒绝了哪个承诺,那么方法spex.batch正是您所需要的。

Batch Processing复制的示例:

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);
    });

输出:

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

现在让我们通过将getWord改为此来使其失败:

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

现在输出是:

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

即。整个数组结算,报告索引限制结果。

如果不是报告完整原因,我们会调用getErrors()

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

然后输出将是:

REASON: [ 'World' ]

这只是为了简化对发生的错误列表的快速访问。

你可以从method's protocol看到你可以传递的可选cb - 回调参数,该参数将告诉你哪些承诺已经解决,哪些承诺被拒绝。

答案 5 :(得分:0)

我同意如果您使用Bluebird,使用Bluebird.reflect实现结算是最好的方法。根据您的使用案例,这是另一种可能很好的解决方案。它在一个有点有趣的情况下对我有用。这也假设Bluebird是承诺库。

就我而言,我有一系列函数(“任务”),这些函数都包含在Promise.method中。这些任务可能会返回被拒绝的承诺,或者它们可能返回或抛出将成为承诺解决方案的普通值。我需要执行所有这些并收集所有结果(不仅仅是第一次失败)。

同样,请记住,tasks数组中的每个项都是Promise.method包装函数。 (易于实施。请参阅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: [] })
}

然后你会这样调用它,找回一个具有一系列已完成值和一系列被拒绝值的对象:

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
  })