为什么JavaScript的'Promise.all`在失败条件下没有运行所有承诺?

seg*_*ult 7 javascript promise es6-promise

MDN称:

如果任何传入的承诺拒绝,则所有承诺立即拒绝拒绝的承诺的价值,放弃所有其他承诺,无论他们是否已经解决.

ES6规格似乎证实了这一点.

我的问题是:如果他们中的任何人拒绝,为什么Promise.all放弃承诺,因为我希望它等待"所有"承诺解决,"丢弃"究竟是什么意思?(很难说"丢弃"对于飞行中的承诺与可能尚未运行的承诺意味着什么.)

我问,因为我经常遇到一些情况,我有一份承诺清单,并希望等待他们全部解决,并得到可能已经发生的所有拒绝,这Promise.all是不满足的.相反,我必须使用这样的黑客:

const promises = []; // Array of promises
const settle = promise => promise.then(result => ({ result }), reason => ({ reason }));
Promise.all(promises.map(settle))
  .then(/ * check "reason" property in each element for rejection */);
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 7

与promises关联的异步操作都已运行.如果其中一个承诺拒绝,那么Promise.all()就不等待所有承诺完成,它会在第一个承诺拒绝时拒绝.这就是它的设计工作方式.如果您需要不同的逻辑(就像您希望等待所有这些逻辑完成,无论它们是满足还是拒绝),那么您就不能使用它Promise.all().

请记住,承诺不是异步操作本身.promise只是一个跟踪异步操作状态的对象.因此,当您传递一系列承诺时Promise.all(),所有这些异步操作都已经启动并且已经在进行中.它们不会被停止或取消.

为什么Promise.all抛弃了承诺,如果他们中的任何一个拒绝,因为我希望它等待"所有"承诺解决.

它的工作方式与它的工作方式相同,因为它是如何设计的,当你不希望代码在有任何错误的情况下继续时,这是一个非常常见的用例.如果它碰巧不是你的用例,那么你需要使用一些.settle()具有你想要的行为的实现(你似乎已经知道了).

我发现更有趣的问题是为什么.settle()规范和标准实现中没有选项,因为它也是一个相当常见的用例.幸运的是,正如您所发现的那样,创建自己的代码并不是很多.当我不需要实际的拒绝原因并且只想将一些指标值放入数组时,我经常使用这个相当简单的版本:

// settle all promises.  For rejeted promises, return a specific rejectVal that is
// distinguishable from your successful return values (often null or 0 or "" or {})
Promise.settleVal = function(rejectVal, promises) {
    return Promise.all(promises.map(function(p) {
        // make sure any values or foreign promises are wrapped in a promise
        return Promise.resolve(p).catch(function(err) {
            // instead of rejection, just return the rejectVal (often null or 0 or "" or {})
            return rejectVal;
        });
    }));
};

// sample usage:
Promise.settleVal(null, someArrayOfPromises).then(function(results) {
    results.forEach(function(r) {
        // log successful ones
        if (r !== null) {
           console.log(r);
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

"丢弃"究竟是什么意思?

它只是意味着承诺不再被追踪Promise.all().与它们相关联的异步操作始终保持正在执行的任何操作.事实上,如果这些承诺上有.then()处理程序,它们就会像通常那样被调用. discard在这里使用似乎是一个不幸的术语.除了Promise.all()停止关注它们之外什么也没发生.


仅供参考,如果我想要一个更强大的版本来.settle()跟踪所有结果并拒绝原因,那么我使用这个:

// ES6 version of settle that returns an instanceof Error for promises that rejected
Promise.settle = function(promises) {
    return Promise.all(promises.map(function(p) {
        // make sure any values or foreign promises are wrapped in a promise
        return Promise.resolve(p).catch(function(err) {
            // make sure error is wrapped in Error object so we can reliably detect which promises rejected
            if (err instanceof Error) {
                return err;
            } else {
                var errObject = new Error();
                errObject.rejectErr = err;
                return errObject;
            }
        });
    }));
}

// usage
Promise.settle(someArrayOfPromises).then(function(results) {
    results.forEach(function(r) {
       if (r instanceof Error) {
           console.log("reject reason", r.rejectErr);
       } else {
           // fulfilled value
           console.log("fulfilled value:", r);
       }
    });
});
Run Code Online (Sandbox Code Playgroud)

这解决了一系列结果.如果结果是instanceof Error,那么它被拒绝,否则它是一个满足的值.


jib*_*jib 6

因为Promise.all保证他们都成功了。就那么简单。

与一起是最有用的构建块Promise.race。其他所有内容都可以基于这些内容。

有没有settle,因为它是如此微不足道打造这样的

Promise.all([a(), b(), c()].map(p => p.catch(e => e)))
Run Code Online (Sandbox Code Playgroud)

没有简单的方法可以Promise.all在之上构建settle,这可能就是为什么它不是默认值的原因。settle还必须标准化一种将成功值与错误区分开的方法,这可能是主观的,并取决于情况。