Promise.settle和承诺履行与拒绝

时间:2015-08-22 19:37:01

标签: javascript promise bluebird

请考虑以下代码,其中包含Bluebird' Promise.settle的简化实现:

kill %1

这将记录" 3个余额中的2个已更新"。为什么这与普通的Promise.all不同?难道不总是实现仍然包含被拒绝的承诺作为其第一个元素吗?

答案似乎在于我对承诺如何运作的困惑。如果我在控制台中创建一个被拒绝的承诺,那么它就是这样:

var a = Promise.reject('a');
var b = Promise.resolve('b');
var c = Promise.resolve('c');

var promises = [a,b,c];

function settled(promises) {
  var alwaysFulfilled = promises.map(function (p) {
    return p.then(
      function onFulfilled(value) {
        return { state: 'fulfilled', value: value };
      },
      function onRejected(reason) {
        return { state: 'rejected', reason: reason };
      }
    );
  });
  return Promise.all(alwaysFulfilled);
}

//Update status message once all requests finish
settled(promises).then(function (outcomes) {
  var count = 0;
  outcomes.forEach(function (outcome) {
    if (outcome.state == 'fulfilled') count++;
  });

  console.log(count + ' out of ' + outcomes.length + ' balances were updated');
});

为什么c"已解决"?

1 个答案:

答案 0 :(得分:4)

理解您的问题的关键是,当您在.then().catch()中提供拒绝处理程序时,您告诉承诺系统您已经"处理"拒绝。因此,除非您的拒绝处理程序本身抛出或返回被拒绝的promise本身,否则该拒绝处理程序的返回值将进入已履行的承诺,而不是被拒绝的承诺。下面解释的更多......

事实上,反过来也是如此。如果您有拒绝处理程序并且基于拒绝类型,您希望拒绝继续传播回被拒绝的承诺,您必须从拒绝处理程序抛出错误或返回被拒绝的承诺。

  

这将记录" 3个余额中的2个已更新"。为什么这样做   不同于简单的Promise.all?

Promise.all()一旦获得承诺列表中的第一次拒绝,就会返回被拒绝的承诺。它不一定返回所有结果,如果您传递的任何承诺被拒绝,它将被拒绝。一旦一个承诺被拒绝,它就基本上放弃了。这是Promise.settle()的重点。它将为您提供所有结果,即使有些被拒绝,您也可以剔除所有结果。

  

不应该总是实现仍然包含被拒绝的承诺   第一个元素?

如下所述,当你在.then()中有拒绝处理程序并且拒绝处理程序没有throw或者返回被拒绝的承诺时(例如它返回正常值,就像你正在做的那样),那么承诺拒绝被视为已处理,并且.then()处理程序产生的承诺得以履行,而不是被拒绝。以下步骤中的更多解释......

  

答案似乎在于我对承诺如何运作的困惑。如果我   在控制台中创建一个被拒绝的承诺,然后就这样......   为什么c"已解决"?

首先,.then()会返回新的承诺。因此a.then()未返回a。它返回了一个新的承诺,它是.then()处理程序中发生的事情的产物。

执行此操作时:

var c = Promise.reject('a').then(undefined, function() {});

以下是发生的事情:

  1. 您使用原因'a'创建被拒绝的承诺。
  2. 您将.then()链接到它,从而创建新的承诺并将其返回到变量c
  3. 然后,因为原始承诺被拒绝,所以调用.then()的第二个处理程序。在调试或设计代码时,请记住这总是异步调用(这有时会使调试器中的人感到困惑)。
  4. 您从拒绝处理程序返回undefined。在这一点上,Promise系统考虑拒绝和处理" .then()的结果是一个履行的承诺,其值为undefined(这是您的处理程序返回的内容)。
  5. 如果您希望结果仍然被拒绝,那么您可以throw或者您可以从拒绝处理程序返回被拒绝的承诺。它以这种方式完成,因此您可以处理"拒绝并保持承诺链成功。此外,未处理的拒绝将导致拒绝的承诺,但是使用拒绝处理程序会告知承诺系统您的代码正在处理拒绝,并且将根据拒绝处理程序的返回结果形成最终的承诺。

    因此,所有这些都会导致c被拒绝:

    // no reject handler
    var c = a.then(function() {});
    
    // throw from reject handler 
    var c = a.then(undefined, function() { throw new Error("whatever")});    
    
    // return rejected promise from reject handler
    var c = a.then(undefined, function() { return Promise.reject("whatever")});   
    

    但是,如果你有一个拒绝处理程序并且它既没有throw也没有返回被拒绝的承诺,那么就会考虑拒绝"处理"并且使用处理程序返回的任何值来解决生成的promise。