3

DeferredjQuery 的API有一个很好的特性,$.wait()可以处理多个Deferreds/ Promises。它在以下情况下返回:

  • 所有Deferreds 都已经resolve()d

或者

  • 其中一个Deferredreject()编辑

大多数时候这是你想要的,但有时你想知道它们什么时候reject()编辑了。

有没有一种简单或优雅的方式来做类似$.wait()但只有当所有Deferreds 都被拒绝时?

(可能还有其他用例,但我的是将其与等待几个 Deferred 中的第一个解决。)

4

2 回答 2

3

本着 Promise 规范未来如何使用PromiseInspection对象的精神,这里有一个 jQuery 附加函数,它告诉你所有承诺何时完成,无论是履行还是拒绝:

// pass either multiple promises as separate arguments or an array of promises
$.settle = function(p1) {
    var args;
    if (Array.isArray(p1)) {
          args = p1;
    } else {
        args = Array.prototype.slice.call(arguments);
    }

    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, value: function() {
                return fulfilled ? val: undefined;
            }, reason: function() {
                return !fulfilled ? val: undefined;
            }
        };
    }
    return $.when.apply($, args.map(function(p) {
        // if incoming value, not a promise, then wrap it in a promise
        if (!p || (!(typeof p === "object" || typeof p === "function")) || typeof p.then !== "function") {
            p = $.Deferred().resolve(p);
        }
        // Now we know for sure that p is a promise
        // Make sure that the returned promise here is always resolved with a PromiseInspection object, never rejected
        return p.then(function(val) {
            return new PromiseInspection(true, val);
        }, function(reason) {
            // convert rejected promise into resolved promise
            // this is required in jQuery 1.x and 2.x (works in jQuery 3.x, but the extra .resolve() is not required in 3.x)
            return $.Deferred().resolve(new PromiseInspection(false, reason));
        });
    })).then(function() {
          // return an array of results which is just more convenient to work with
          // than the separate arguments that $.when() would normally return
        return Array.prototype.slice.call(arguments);
    });
}

然后,您可以像这样使用它:

$.settle(promiseArray).then(function(inspectionArray) {
    inspectionArray.forEach(function(pi) {
        if (pi.isFulfilled()) {
            // pi.value() is the value of the fulfilled promise
        } else {
            // pi.reason() is the reason for the rejection
        }
    });
});

请记住,$.settle()将始终履行(从不拒绝)并且履行的值是一个PromiseInspection对象数组,您可以询问每个对象以查看它是履行还是拒绝,然后获取相应的值或原因。有关示例用法,请参见下面的演示:

工作演示:https ://jsfiddle.net/jfriend00/y0gjs31r/

于 2016-03-05T22:00:00.293 回答
0

我最近遇到了同样的问题:我认为这是因为$.when更多的是一种Promise.all实现。

我无法Promise.allSettled在 jQuery 文档和整个网络中找到替代方案,所以这是我的尝试:

const deferredsAllSettled = deferreds => {
    const settlements = [];

    deferreds.forEach(deferred => {
        const settlement = jQuery.Deferred();

        deferred.always(settlement.resolve);

        settlements.push(settlement);
    });

    const returnedDeferred = $.Deferred();

    jQuery.when
        .apply(jQuery, settlements)
        .then(() =>
            returnedDeferred.resolve(
                deferreds.map(deferred => deferred.promise())
            )
        );

    return returnedDeferred.promise();
};

注意:我已经为 utils 文件编写了该文件,因此Promise.allSettled唯一的可迭代参数签名对我来说是可以的,如果您想在$.when精神上编写它(将$.Deferreds 作为单独的参数传递和获取),您必须编写一个再多一点...

于 2020-03-07T13:16:36.157 回答