Promise.settle 和承诺履行与拒绝

Promise.settle and promise fulfillment vs rejection

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

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

这将记录“更新了 3 个余额中的 2 个”。为什么这与普通的 Promise.all 不同? alwaysFulfilled 不应该仍然包含一个被拒绝的承诺作为它的第一个元素吗?

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

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

a
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: "a"}
b
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: "a"}
c
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}

为什么是c"resolved"?

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

其实反之亦然。如果你有一个拒绝处理程序并且基于拒绝的类型,你希望拒绝继续传播回被拒绝的承诺,你必须从拒绝处理程序中抛出错误或 return 一个被拒绝的承诺。

This will log "2 out of 3 balances were updated". Why does this work differently than a plain Promise.all?

Promise.all() return一旦在给定的承诺列表中获得第一个拒绝,它就会被拒绝。它不一定 return 所有结果,如果你通过它的任何承诺被拒绝,它 return 就会被拒绝。一旦一个承诺被拒绝,它基本上就放弃了。这就是 Promise.settle() 的意义所在。它会为您提供所有结果,即使有些结果被拒绝,您也可以筛选所有结果。

Shouldn't alwaysFulfilled still contain a rejected promise as its first element?

如下所述,当您在 .then() 中有一个拒绝处理程序并且该拒绝处理程序没有 throw 或 return 被拒绝的承诺(例如它 return s 是一个正常值,就像您正在做的那样),那么该承诺拒绝被视为已处理,并且来自 .then() 处理程序的结果承诺得到履行,而不是被拒绝。下面的步骤中有更多解释...

The answer seems to lie in my confusion over how promises work. If I create a rejected promise in the console and .then it like so... Why is c "resolved"?

首先,.then() return 是一个新的承诺。所以 a.then() 不是 returning a。这是 return 一个新的承诺,它是 .then() 处理程序中发生的事情的产物。

当你这样做时:

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

这是正在发生的事情:

  1. 您创建了一个被拒绝的承诺,原因是 'a'
  2. 您将 .then() 链接到它,这会创建一个新的承诺,并且 return 将其放入您的变量 c
  3. 然后,因为最初的承诺被拒绝,所以调用 .then() 的第二个处理程序。在调试或设计代码时,请记住这始终是异步调用的(这有时会使调试器中的人感到困惑)。
  4. 你 return undefined 来自拒绝处理程序。此时,Promise 系统认为拒绝 "handled" 并且 .then() 的结果是具有 undefined 值的已履行承诺(这就是您的处理程序 returned)。

如果您希望结果仍然被拒绝,那么您可以 throw 或者您可以 return 来自拒绝处理程序的拒绝承诺。以这种方式完成,因此您可以 "handle" 拒绝并保持承诺链成功运行。此外,未处理的拒绝将导致拒绝的承诺,但是拒绝处理程序会告诉承诺系统您的代码正在处理拒绝,并且将根据拒绝处理程序的 return 结果形成最终的承诺.

所以,所有这些都会导致 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 也没有 return 被拒绝的承诺,那么拒绝被视为 "handled" 并且最终的承诺被解决无论你的处理程序有什么价值 returns.