ES6 Promise.all() 错误句柄 - 需要 .settle() 吗? [英] ES6 Promise.all() error handle - Is .settle() needed?

查看:24
本文介绍了ES6 Promise.all() 错误句柄 - 需要 .settle() 吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Let's say I have a Promise.all() that handles two promises. If one promise produces an error, but the other resolves, I would like to be able to handle the errors based on the situation after the Promise.all() has settled.

ES6 Promise 缺少结算方法,我假设是有充分理由的.但我不禁想到 .settle() 方法会让这个问题对我来说容易很多.

ES6 Promises are missing the settle method, I'm assuming for a good reason. But I can't help but think that the .settle() method would make this problem a lot easier for me.

我是否以错误的方式处理这个问题,还是使用 set 方法扩展 ES6 Promises 是正确的做法?

Am I going about this the wrong way or is extending the ES6 Promises with a settle method the right thing to do here?

我正在考虑如何使用 .settle() 的一个例子:

An example of how I am thinking of using .settle():

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

推荐答案

我是在以错误的方式处理这个问题还是扩展了 ES6 Promises在这里使用结算方法正确吗?

Am I going about this the wrong way or is extending the ES6 Promises with a settle method the right thing to do here?

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

You can't directly use Promise.all() to generate .settle() type behavior that gets you all the results whether any reject or not because Promise.all() is "fast-fail" and returns as soon as the first promise rejects and it only returns that reject reason, none of the other results.

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

So, something different is needed. Often times, the simplest way to solve that problem is by just adding a .then() handler to whatever operation creates your array of promises such that it catches any rejects and turns them into fulfilled promises with some specific value that you can test for. But, that type of solution is implementation dependent as it depends upon exactly what type of value you are returning so that isn't entirely generic.

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

If you want a generic solution, then something like .settle() is quite useful.

你不能使用结构:

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

注意(于 2019 年添加):似乎 Promise 标准工作选择了 Promise.allSettled() 作为类似定居"行为的标准实现.您可以在此答案的末尾看到更多相关信息.

因为 Promise.all() 在您传递给它的第一个承诺拒绝时拒绝,并且它只返回那个拒绝..settle() 逻辑的工作原理如下:

Because Promise.all() rejects when the first promise you pass it rejects and it returns only that rejection. The .settle() logic works like:

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

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

And, if you're interested, here's a fairly simple implementation of 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() 处理程序附加到传入的每个承诺中,该处理程序处理该承诺的解析或拒绝,并将结果放入 PromiseInspection 对象中,然后该对象变为承诺的解析值.

In this implementation, Promise.settle() will always resolve (never reject) and it resolves with an array of PromiseInspection objects which allows you to test each individual result to see whether it resolved or rejected and what was the value or reason for each. It works by attaching a .then() handler to each promise passed in that handles either the resolve or reject from that promise and puts the result into a PromiseInspection object which then becomes the resolved value of the promise.

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

You would then use this implementation like this;

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(),我经常发现它在您不需要时更容易使用实际拒绝原因,您只想知道给定的数组槽是否被拒绝.在此版本中,您传入一个默认值,该值应替换任何被拒绝的承诺.然后,您只会得到一个返回值的平面数组,以及任何设置为被拒绝的默认值的值.例如,您通常可以选择 null0""{} 的 rejectVal 并且它使结果更容易处理.这是函数:


FYI, I've written another version of .settle myself that I call .settleVal() and I often find it easier to use when you don't need the actual reject reason, you just want to know if a given array slot was rejected or not. In this version, you pass in a default value that should be substituted for any rejected promise. Then, you just get a flat array of values returned and any that are set to the default value where rejected. For example, you can often pick a rejectVal of null or 0 or "" or {} and it makes the results easier to deal with. Here's the function:

// 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;
        });
    }));
};

然后,您可以像这样使用它:

And, then you use it like this:

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% 以上的情况下,这更易于使用.

This isn't an entire replacement for .settle() because sometimes you may want to know the actual reason it was rejected or you can't easily distinguish a rejected value from a non-rejected value. But, I find that more than 90% of the time, this is simpler to use.

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

Here's my latest simplification for .settle() that leaves an instanceof Error in the return array as the means of distinguishing between resolved values and rejected errors:

// 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;
        });
    }));
};

然后,您可以像这样使用它:

And, then you use it like this:

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

This can be a complete replacement for .settle() for all cases as long as an instanceof Error is never a resolved value of your promises (which it really shouldn't be).

承诺标准的努力

截至 2019 年,.allSettled() 正在成为这种行为的标准.而且,这是一个 polyfill:

As of 2019, it appears that .allSettled() is becoming the standard for this type of behavior. And, here's a 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() 处理程序可能会抛出或返回一个被拒绝的承诺,使整个链拒绝.

Note that Promise.allSettled() itself always resolves, never rejects though subsequent .then() handlers could throw or return a rejected promise to make the whole chain reject.

截至 2019 年 6 月,目前的桌面版 Chrome 浏览器尚未提供此功能,但计划在即将发布的版本(例如 2019 年晚些时候)中提供.

As of June 2019, this not yet in the current desktop Chrome browser, but is planned for an upcoming release (e.g. later in 2019).

这篇关于ES6 Promise.all() 错误句柄 - 需要 .settle() 吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆