$ .Deferred:如何检测每个承诺何时执行
我有许多需要完成的异步任务,所以我使用的是promises。
我需要检测每个承诺何时被执行(解决和拒绝)。 在此之前我不能继续执行。
我使用的是这样的东西:
$.when(promise1, promise2, ...).always();
但是这段代码是错误的,因为when
方法具有惰性求值,并且只要其中一个promise失败就会返回。 因此,只要其中一个承诺失败, always
回调也会运行。
我正在考虑编写一个变通方法,但是这个用例很常见,可能有人已经完成了,或者甚至有一种方法可以使用jQuery(如果没有,添加Promise.whenNonLazy
或者很好) Promise.when(promise1, promise2, ..., false)
。
这可能吗?
更复杂的promise库有一个像Bluebird 这样的Q
或Promise.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/