6

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

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

我正在使用这样的东西:

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

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

我正在考虑编写一个解决方法,但是这个用例很常见,也许有人已经这样做了,或者甚至有一种方法可以只使用 jQuery 来做到这一点(如果没有,Promise.whenNonLazy最好Promise.when(promise1, promise2, ..., false)在未来。

这可能吗?

4

3 回答 3

7

更复杂的 Promise 库具有allSettled()类似QPromise.settle类似 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稍微更改回调以区分 final 中的已完成和已拒绝结果done

于 2013-10-04T13:06:37.910 回答
1

这是一个有趣的属性always——我没想到会有这种行为。

我想你可以使用一个 master 的顶级 deferred 来监控主要 deferred 的状态,只有在 main deferred 全部解决或拒绝后才会解决。就像是:

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

于 2013-10-04T09:37:01.327 回答
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()来区分已解决和已拒绝的 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()回调的每个参数,其对应的承诺最初是被解决还是被拒绝。这就是您将拒绝转化为解决方案所付出的代价。但是,您可能能够从解决/拒绝原始承诺的参数中检测到原始状态。

于 2013-10-04T14:20:02.557 回答