我有许多需要完成的异步任务,所以我正在使用promises。
我需要检测每个承诺的执行时间(解决和拒绝)。在此之前我不能继续执行。
我使用的是这样的东西:
$.when(promise1, promise2, ...).always();
但是这段代码是错误的,因为when
方法具有惰性求值,并且只要其中一个promise失败就会返回。因此,只要其中一个承诺失败,always
回调也会立即运行。
我正在考虑编写一个变通方法,但是这个用例很常见,可能有人已经完成了,或者甚至有一种方法可以使用jQuery(如果没有,添加一个{ <1}}或将来Promise.whenNonLazy
。
这可能吗?
答案 0 :(得分:7)
更复杂的承诺库有allSettled()
function like Q
或Promise.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();
答案 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()
回调的每个参数,是否最初解决或拒绝其相应的承诺。这是您将拒绝转换为解决方案所付出的代价。但是,您可以从原始承诺被解决/拒绝的参数中检测原始状态。