ES6 Promise.all()错误句柄 - 是否需要.settle()?

时间:2016-04-13 17:13:48

标签: javascript promise ecmascript-6 es6-promise

假设我有一个处理两个承诺的Promise.all()。如果一个承诺产生错误,但另一个承诺解决,我希望能够在Promise.all()结算后根据情况处理错误。

ES6承诺缺少结算方法,我假设有充分理由。但我不禁想到.settle()方法会让我更容易解决这个问题。

我是以错误的方式解决这个问题,还是使用解决方法扩展ES6 Promise是正确的做法?

我正在考虑使用.settle()

的示例
Promise.all([Action1,Action2])
.settle(function(arrayOfSettledValues) 
    //if 1 failed but not 2, handle
    //if 2 failed but not 1, handle
    //etc....
)

1 个答案:

答案 0 :(得分:15)

  

我是以错误的方式解决这个问题还是正在扩展ES6承诺   用一种解决方法在这里做正确的事情?

您无法直接使用Promise.all()生成.settle()类型的行为,无论是否有任何拒绝都可以获得所有结果,因为Promise.all()是"快速失败& #34;并且在第一个承诺拒绝后立即返回,并且它仅返回拒绝原因,而不返回其他结果。

因此,需要不同的东西。通常情况下,解决该问题的最简单方法是在任何操作中添加一个.then()处理程序来创建您的承诺数组,以便捕获任何拒绝并将其转换为具有某些特定值的履行承诺,您可以测试。但是,这种类型的解决方案依赖于实现,因为它取决于您返回的确切类型的值,因此它不是完全通用的。

如果你想要一个通用的解决方案,那么像.settle()这样的东西非常有用。

您无法使用该结构:

Promise.all([...]).settle(...).then(...);

因为Promise.all()拒绝你传递的第一个承诺时拒绝它,它只返回拒绝。 .settle()逻辑的工作方式如下:

Promise.settle([...]).then(...);

而且,如果您有兴趣,这里是Promise.settle()的一个相当简单的实现:

// ES6 version of settle
Promise.settle = function(promises) {
    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, isRejected: function() {
                return !fulfilled;
            }, isPending: function() {
                // PromiseInspection objects created here are never pending
                return false;
            }, value: function() {
                if (!fulfilled) {
                    throw new Error("Can't call .value() on a promise that is not fulfilled");
                }
                return val;
            }, reason: function() {
                if (fulfilled) {
                    throw new Error("Can't call .reason() on a promise that is fulfilled");
                }
                return val;
            }
        };
    }

    return Promise.all(promises.map(function(p) {
        // make sure any values are wrapped in a promise
        return Promise.resolve(p).then(function(val) {
            return new PromiseInspection(true, val);
        }, function(err) {
            return new PromiseInspection(false, err);
        });
    }));
}

在此实现中,Promise.settle()将始终解析(永不拒绝)并使用PromiseInspection个对象进行解析,这些对象允许您测试每个结果,以查看它是已解决还是拒绝,以及每个人的价值或原因。它的工作原理是将.then()处理程序附加到传递的每个promise中,处理来自该promise的解析或拒绝,并将结果放入PromiseInspection对象,然后该对象成为promise的已解析值。

然后你会像这样使用这个实现;

Promise.settle([...]).then(function(results) {
    results.forEach(function(pi, index) {
        if (pi.isFulfilled()) {
            console.log("p[" + index + "] is fulfilled with value = ", pi.value());
        } else {
            console.log("p[" + index + "] is rejected with reasons = ", pi.reason());
        }
    });
});

仅供参考,我自己写了.settle的另一个版本,我称之为.settleVal()我经常发现当您不需要实际的拒绝原因时更容易使用只是想知道给定的数组插槽是否被拒绝。在此版本中,您传入一个默认值,该值应替换任何被拒绝的承诺。然后,您只需返回一个返回值的平面数组,并将所有值设置为拒绝的默认值。例如,您通常可以选择rejectVal null0""{},这样可以更轻松地处理结果。这是功能:

// settle all promises.  For rejected 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).then(null, function(err) {
            // instead of rejection, just return the rejectVal (often null or 0 or "" or {})
            return rejectVal;
        });
    }));
};

然后,你就这样使用它:

Promise.settleVal(null, [...]).then(function(results) {
    results.forEach(function(pi, index) {
        if (pi !== null) {
            console.log("p[" + index + "] is fulfilled with value = ", pi);
        }
    });
});

这不是.settle()的完整替代品,因为有时您可能想知道它被拒绝的实际原因,或者您无法轻易区分被拒绝的值和未被拒绝的值。但是,我发现超过90%的时间,使用起来更简单。

这是我对.settle()的最新简化,它在返回数组中留下instanceof Error作为区分已解析值和被拒绝错误的方法:

// settle all promises.  For rejected promises, leave an Error object in the returned array
Promise.settleVal = 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) {
            let returnVal = err;
            // instead of rejection, leave the Error object in the array as the resolved value
            // make sure the err is wrapped in an Error object if not already an Error object
            if (!(err instanceof Error)) {
                returnVal = new Error();
                returnVal.data = err;
            }
            return returnVal;
        });
    }));
};

然后,你就这样使用它:

Promise.settleVal(null, [...]).then(function(results) {
    results.forEach(function(item, index) {
        if (item instanceof Error) {
            console.log("p[" + index + "] rejected with error = ", item);
        } else {
            console.log("p[" + index + "] fulfilled with value = ", item);
        }
    });
});

对于所有情况,只要.settle()永远不是您承诺的解决价值(它实际上不应该是),这可以完全取代instanceof Error