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

时间:2013-10-04 08:53:58

标签: javascript jquery asynchronous promise jquery-deferred

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

我需要检测每个承诺的执行时间(解决和拒绝)。在此之前我不能继续执行。

我使用的是这样的东西:

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

但是这段代码是错误的,因为when方法具有惰性求值,并且只要其中一个promise失败就会返回。因此,只要其中一个承诺失败,always回调也会立即运行。

我正在考虑编写一个变通方法,但是这个用例很常见,可能有人已经完成了,或者甚至有一种方法可以使用jQuery(如果没有,添加一个{ <1}}或将来Promise.whenNonLazy

这可能吗?

3 个答案:

答案 0 :(得分:7)

更复杂的承诺库有allSettled() function like QPromise.settle like Bluebird

在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中的已完成和已拒绝的结果。

答案 1 :(得分:1)

这是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/

答案 2 :(得分:1)

史密斯,

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

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()回调的每个参数,是否最初解决或拒绝其相应的承诺。这是您将拒绝转换为解决方案所付出的代价。但是,您可以从原始承诺被解决/拒绝的参数中检测原始状态。