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

Ell*_*iot 9 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....
)
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 17

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

您不能直接使用Promise.all()生成.settle()类型行为来获取所有结果,无论是否为拒绝,因为Promise.all()"快速失败"并在第一个承诺拒绝后立即返回,并且它只返回拒绝原因,没有其他结果.

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

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

你不能使用这个结构:

Promise.all([...]).settle(...).then(...);
Run Code Online (Sandbox Code Playgroud)

因为Promise.allSettled()当你传递的第一个承诺拒绝而拒绝它并且它只返回拒绝.该Promise.all()逻辑工作方式:

Promise.settle([...]).then(...);
Run Code Online (Sandbox Code Playgroud)

而且,如果您有兴趣,这里有一个相当简单的实现.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);
        });
    }));
}
Run Code Online (Sandbox Code Playgroud)

在此实现中,Promise.settle()将始终解析(从不拒绝)并使用一组Promise.settle()对象进行解析,这些对象允许您测试每个单独的结果以查看它是否已解决或拒绝,以及每个结果的价值或原因是什么.它的工作原理是将一个PromiseInspection处理程序附加到传递的每个promise中,该promise处理该promise的解析或拒绝,并将结果放入一个.then()对象,然后该对象成为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());
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

仅供参考,我已经编写了PromiseInspection我自己的另一个版本,.settle当你不需要实际的拒绝原因时,我常常发现它更容易使用,你只想知道给定的数组插槽是否被拒绝.在此版本中,您传入一个默认值,该值应替换任何被拒绝的承诺.然后,您只需返回一个返回值的平面数组,并将任何设置为拒绝的默认值.例如,你经常可以挑选.settleVal()rejectValnull0""它使结果更容易对付.这是功能:

// 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;
        });
    }));
};
Run Code Online (Sandbox Code Playgroud)

然后,你就像这样使用它:

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

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


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

// 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;
        });
    }));
};
Run Code Online (Sandbox Code Playgroud)

然后,你就像这样使用它:

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);
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

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

  • @Bergi - 我更新了代码,总是调用`Promise.resolve()`并删除`isPromise()`调用.绝对简单. (2认同)