ES6 Promise.al()错误句柄-Is.settle()需要

ES6 Promise.all() error handle - Is .settle() needed?

本文关键字:-Is settle 需要 句柄 错误 Promise ES6 al      更新时间:2024-06-17

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

ES6 Promises缺少settle方法,我认为这是有充分理由的。但我忍不住认为.settle()方法会让这个问题对我来说容易得多

我这样做是错误的,还是用结算方法扩展ES6 Promises是正确的做法?

我如何考虑使用.settle():的示例

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

我这样做是错误的还是在延长ES6承诺用一种解决方法在这里做正确的事情?

您不能直接使用Promise.all()来生成.settle()类型的行为,无论是否拒绝,都可以获得所有结果,因为Promise.all()是"快速失败"的,并且在第一个promise拒绝时立即返回,它只返回拒绝原因,而不返回其他结果。

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

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

您不能使用以下结构:

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

注意(添加于2019年):Promise标准工作似乎选择了Promise.allSettled()作为"类似结算"行为的标准实现。你可以在这个答案的末尾看到更多关于这方面的内容

因为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(),当你不需要实际的拒绝原因时,我经常发现它更容易使用,你只想知道给定的阵列插槽是否被拒绝。在这个版本中,您传递了一个默认值,该值应该替换任何被拒绝的promise。然后,您只需要返回一个值的平面数组,任何被设置为默认值的值都会被拒绝。例如,您通常可以选择null0""{}中的rejectVal,这样可以更容易地处理结果。功能如下:

// 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永远不是你承诺的解决价值(它真的不应该是)。


承诺标准努力

截至2019年,.allSettled()似乎正在成为这类行为的标准。这里有一个polyfill:

if (!Promise.allSettled) {
    Promise.allSettled = function(promises) {
        let wrappedPromises = Array.from(promises).map(p => 
             this.resolve(p).then(
                 val => ({ state: 'fulfilled', value: val }),
                 err => ({ state: 'rejected', reason: err })
             )
        );
        return this.all(wrappedPromises);
    }
}

用法如下:

let promises = [...];    // some array of promises, some of which may reject
Promise.allSettled(promises).then(results => {
    for (let r of results) {
        if (r.state === 'fulfilled') {
            console.log('fulfilled:', r.val);
        } else {
            console.log('rejected:', r.err);
        }
    }
});

请注意,Promise.allSettled()本身总是解析,从不拒绝,尽管随后的.then()处理程序可以抛出或返回一个被拒绝的promise以使整个链拒绝。

截至2019年6月,这还没有出现在当前的桌面Chrome浏览器中,但计划在即将发布(例如2019年晚些时候)。