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

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

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

我使用的是这样的东西:

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

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

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

这可能吗?

更复杂的promise库有一个像Bluebird 这样的QPromise.settle类的allSettled()函数 。

在jQuery中,您也可以自己实现这样的函数,并使用它扩展$ namespace,但只有在经常需要它并进行性能优化时才需要这样做。

一个更简单的解决方案是为你正在等待的每一个创建一个新的承诺,并且即使在底层被拒绝的情况下也要实现它们。 然后你可以毫无问题地使用$.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中的已完成和已拒绝的结果。

铁匠铺,

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

 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()的特性来区分已解决的和被拒绝的promise,并做出相应的响应。

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

这是一个有趣的财产 – 我没想到这种行为。

我想你可以使用一个主延迟来监控主要延期的状态,只有在主延期全部被解决或被拒绝后才能解决。 就像是:

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