【问题标题】:Promise.settle and promise fulfillment vs rejectionPromise.settle 和承诺履行与拒绝
【发布时间】:2015-11-16 13:31:28
【问题描述】:

考虑以下包含 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 被“解析”了?

【问题讨论】:

    标签: javascript promise bluebird


    【解决方案1】:

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

    事实上,反之亦然。如果您有一个拒绝处理程序并且根据拒绝的类型,您希望拒绝继续传播回被拒绝的承诺,您必须从拒绝处理程序中抛出错误或返回被拒绝的承诺。

    这将记录“已更新 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 值的已履行承诺(这是您的处理程序返回的内容)。

    如果你希望结果仍然被拒绝,那么你可以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 也没有返回一个被拒绝的承诺,那么拒绝被认为是“已处理的”并且生成的承诺将使用您的处理程序返回的任何值来解决。

    【讨论】:

    • @Bergi - 我可能不小心打败了你的编辑。我对版本历史感到困惑,所以请随时更正。我从来没有对何时使用“已解决”以及何时使用“已完成”感到满意,这看起来就像您要解决的问题。
    • 是的,这只是次要的。 “解决”和“实现”之间的区别是微妙的……见these explanations
    猜你喜欢
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 2014-06-04
    • 2018-05-05
    • 2020-07-30
    • 2019-11-25
    • 2021-03-14
    • 1970-01-01
    相关资源
    最近更新 更多