当前位置: 首页 > 知识库问答 >
问题:

ES6 Promise.all()错误句柄-是否需要.solite()?[副本]

罗奇文
2023-03-14

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

我认为ES6promise缺少结算方法是有充分理由的。但是我忍不住想,.settle()方法会让我更容易解决这个问题。

我是不是走错了路,还是用解决方法扩展ES6promise是正确的做法?

关于我如何使用.solite()的示例:

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

共有1个答案

章涵容
2023-03-14

我是不是走错了路,还是用解决方法扩展ES6promise是正确的做法?

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

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

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

您不能使用以下结构:

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

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

因为当您传递的第一个promise拒绝时,Promise.all()会拒绝,并且只返回拒绝。该. setp()逻辑的工作原理如下:

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

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

// 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()将始终解析(从不拒绝),它用一个数组来解析,允许您测试每个单独的结果,看看它是解析还是拒绝,以及什么是每个的价值或原因。它的工作原理是将一个。然后()处理程序附加到传递的每个promise,该处理程序处理该promise的解析或拒绝,并将结果放入一个promise检查对象中,该对象随后成为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。然后,您只需得到一个返回值的平面数组,以及任何设置为默认值的值(如果被拒绝)。例如,您可以经常选择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;
        });
    }));
};

然后你就这样使用它:

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

这并不是对.solite()的完全替代,因为有时您可能想知道它被拒绝的实际原因,或者您无法轻松区分被拒绝的值和未被拒绝的值。但是,我发现在90%以上的情况下,这更容易使用。

下面是我对.solite()的最新简化,它在返回数组中留下一个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);
        }
    });
});

在所有情况下,只要instanceof Error从来不是promise的解析值(它真的不应该是),这都可以完全替换

promise标准努力

自2019年起,.allsolided()似乎正在成为此类行为的标准。这是一个多边形填充:

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.allsolited()本身始终解析,从不拒绝后续的。然后()处理程序可以抛出或返回被拒绝的promise,以使整个链拒绝。

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

 类似资料:
  • 问题内容: 假设我有一个处理两个诺言的。如果一个承诺产生了错误,而另一个承诺解决了,我希望能够根据解决后的情况来处理错误。 我认为ES6 Promises缺少解决方法,这是有充分理由的。但是我不禁认为该方法会使我更容易解决此问题。 我是用错误的方式解决问题还是用解决方法扩展ES6 Promises在这里正确的事情? 我正在考虑使用的示例: 问题答案: 我是用错误的方式解决问题还是用解决方法扩展ES

  • 问题内容: 对于我因此遇到的所有Dockerfile(虽然数量不多),它们所有人都使用了子句作为现有映像的基础,即使它是。 这个条款是必需的吗?有没有子句的Dockerfile是否可能?这样创建的容器可以执行任何操作吗? 编辑 我读 没有FROM指令的Dockerfile没有父映像,称为基本映像。 https://docs.docker.com/glossary/?term=parent%20im

  • 试图移动到一个新的服务器,我有这么多的麻烦!尝试安装请求mcrypt扩展时出错。我已将extension=mcrypt.so添加到我的php.ini文件中,但仍然出现以下错误: 有人能帮忙吗?

  • 问题内容: 以下语句在我的数据库中有效: 但这不是: 我得到了错误: 错误:“ my_table.column_a”列必须出现在GROUP BY子句中或在聚合函数中使用 有用的注释:该线程:SQL子句“ GROUP BY1”是什么意思? 讨论“ ”的含义。 更新: 我感到困惑的原因是因为我经常看到如下情况: 那里是 没有 说法。是否总是需要跟着 ?在这种情况下,该语句是否隐含? 问题答案: 这个错

  • 问题内容: 我只是在浏览一个论坛,有人问他们在网上找到的PHP文件。在代码中有几个这样的地方: 我一直认为,如果条件为真,则需要用括号括起来。还有其他选择吗,例如您是否不在同一行? 还有另一行是这样的: 我的本能是说这行不通,但是我也不知道它是否是过时的PHP文件并且它曾经可以工作? 问题答案: 您可以执行以下其他语句: 然后您还可以编写if-else的替代语法: 使用备用语法,您也可以退出解析模

  • 随着新的工具栏小部件的引入和AppCompat(android.support.v7.widget.Toolbar)版本的推出,是否还需要调用setSupportActionbar(工具栏)?或者调用setSupportActionbar有什么好处吗;现在我们可以设置标题、子标题、导航图标、导航图标单击侦听器(getSupportActionBar()。直接在工具栏上设置DisplayHomeAs