$.延迟:如何检测每个承诺何时执行

$.Deferred: How to detect when every promise has been executed

本文关键字:承诺 执行 何时 检测 延迟 何检测      更新时间:2023-09-26

我有许多异步任务需要完成,所以我正在使用 promises。

我需要检测每个承诺何时被执行(已解决和拒绝(。在那之前,我绝不能继续执行。

我使用了这样的东西:

$.when(promise1, promise2, ...).always();

但是这段代码是错误的,因为 when 方法具有延迟计算,并且一旦其中一个承诺失败,它就会返回。因此,一旦其中一个承诺失败,always回调也会立即运行。

我正在考虑编写一种解决方法,但是这个用例非常普遍,也许有人已经这样做了,或者甚至有一种方法可以仅使用jQuery来做到这一点(如果没有,将来最好添加一个Promise.whenNonLazyPromise.when(promise1, promise2, ..., false)

这可能吗?

更复杂的 promise 库具有像 Q 这样的allSettled()函数或像 Bluebird 这样的Promise.settle函数。

在 jQuery 中,您也可以自己实现这样的函数,并使用它扩展$命名空间,但这只有在您经常需要它并且性能优化时才是必需的。

一个更简单的解决方案是为您正在等待的每个承诺创建一个新的承诺,即使潜在的承诺被拒绝,也要履行它们。然后,您可以毫无问题地对它们使用$.when()。总之:

// using Underscore's .invoke() method:
$.when.apply(null, _.invoke(promises, "then", null, $.when)).done(…)

更稳定:

$.when.apply($, $.map(promises, function(p) {
    return p.then(null, function() {
        return $.Deferred().resolveWith(this, arguments);
    });
})).then(…);

您可以稍微更改then回调,以区分最终done中已实现和拒绝的结果。

Smithy,

首先,让我们假设你的承诺在一个数组中。

var promises = [....];

你似乎想要的.when()应用于这些承诺的某种转换,这样任何被拒绝的承诺都会转换为已解决的承诺,同时对已经解决的承诺是透明的。

所需的操作可以非常简洁地编写如下:

$.when.apply(null, $.map(promises, resolvize)).done(...);
//or, if further filtering by .then() is required ...
$.when.apply(null, $.map(promises, resolvize)).then(...);

其中resolvize是转换机制。

那么resolvize()应该是什么样子呢?让我们利用.then()的特点来区分已解决的承诺和被拒绝的承诺,并做出相应的回应。

function resolvize(promise) {
    //Note: null allows a resolved promise to pass straight through unmolested;
    return promise.then(null, function() {
        return $.Deferred().resolve.apply(null, arguments).promise();
    });
}

未经测试

通过在某些外部作用域中的resolvize,它可以在需要时用于$.when.apply($.map(promises, resolvize))表达式中。这很可能是足够的,而无需使用新方法扩展jQuery。

无论转换是如何实现的,你最终都会遇到一个潜在的问题;即知道.done()回调的每个参数,其相应的承诺最初是被解决还是被拒绝。这就是您将拒绝转换为解决方案所付出的代价。但是,您可以从解决/拒绝原始承诺的参数中检测原始状态。

这是always的一个有趣的属性 - 我没想到会有这种行为。

我想您可以使用主的顶级延迟来监视主要延迟的状态,只有在主要延迟全部解决或拒绝后才能解决。像这样:

//set up master deferred, to observe the states of the sub-deferreds
var master_dfd = new $.Deferred;
master_dfd.done(function() { alert('done'); });
//set up sub-deferreds
var dfds = [new $.Deferred, new $.Deferred, new $.Deferred];
var cb = function() {
    if (dfds.filter(function(dfd) {
        return /resolved|rejected/.test(dfd.state());
    }).length == dfds.length)
        master_dfd.resolve();
};
dfds.forEach(function(dfd) { dfd.always(cb); });
//resolve or reject sub-deferreds. Master deferred resolves only once
//all are resolved or rejected
dfds[0].resolve();
dfds[1].reject();
dfds[2].resolve();

小提琴:http://jsfiddle.net/Wtxfy/3/