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

Javascript $.Deferred:如何检测何时执行了每个承诺,javascript,jquery,asynchronous,promise,jquery-deferred,Javascript,Jquery,Asynchronous,Promise,Jquery Deferred,我有许多异步任务需要完成,所以我使用承诺 我需要检测每一个承诺何时被执行(包括被解决和拒绝)。在那之前我不能继续执行死刑 我用的是这样的东西: $.when(promise1, promise2, ...).always(); 但是这段代码是错误的,因为when方法具有延迟求值,并且一旦其中一个承诺失败,它就会返回。因此,总是回调也会在其中一个承诺失败时运行 我在考虑编写一个变通方法,但是这个用例非常常见,可能有人已经这样做了,或者甚至有一种方法可以使用jQuery(如果没有,最好添加一个Pr

我有许多异步任务需要完成,所以我使用承诺

我需要检测每一个承诺何时被执行(包括被解决和拒绝)。在那之前我不能继续执行死刑

我用的是这样的东西:

$.when(promise1, promise2, ...).always();
但是这段代码是错误的,因为
when
方法具有延迟求值,并且一旦其中一个承诺失败,它就会返回。因此,
总是
回调也会在其中一个承诺失败时运行

我在考虑编写一个变通方法,但是这个用例非常常见,可能有人已经这样做了,或者甚至有一种方法可以使用jQuery(如果没有,最好添加一个
Promise.whenOnly
Promise.when(promise1,promise2,…,false)


这是可能的吗?

这是
的一个有趣的特性,总是
——我没想到会有这种行为

我想您可以使用一个主延迟、顶级延迟来监控主要延迟的状态,只有在主要延迟全部解决或被拒绝后才能解决。类似于:

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

Fiddle:

更复杂的promise库有一个or

在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(…);
您可以更改
然后
回调一点,以便在最终的
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()
应该是什么样子呢?让我们利用
的特性。然后()
来区分已解决的承诺和被拒绝的承诺,并做出相应的响应

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

是的,这是基本想法。我正在做一件类似的事情:我为每个延迟创建一个延迟的包装,然后在包含的延迟解决或被拒绝时解决包装。最后应用常规的
$。当
覆盖所有包装时。我仍然需要解决如何解决将已解析的参数传递回主延迟(当
承诺,如果已解析,将收到每个承诺的单个参数列表时,
$)。我想在我的
方法中执行同样的操作。
.always()
只接受一个回调,为什么要传递两个呢?使用
.when()
以多个承诺作为参数,表示您对承诺的组合履行感兴趣。如果任何一个承诺被拒绝,则
when()
返回的承诺将被拒绝。因此,您观察到的行为是
when()
而不是
始终()
。when()
.always()
都是懒惰的。两者都是完全正确的。不清楚你想要什么行为,但似乎你不应该使用
when()
@Beetroot Beetroot你是对的,我编辑了这个问题。但我认为
when
显示懒惰行为。为什么“懒惰”而不是“精力充沛”?:-)这是一个重复的行为,在这里有充分的解释。我想我明白了,但是
$。Deferred
是一个函数,您可能需要
$。Deferred()
。另外,我认为您需要将成功回调传递给
p。然后
这将与错误回调相同。好的,我刚刚理解了
然后
中的
null
参数。它允许已解决的承诺按原样返回。但是,
$.Deferred
肯定需要括号。如果您想插入
$.solite()
的代码,这里有一个: