等待多个承诺被拒绝

Wait for multiple promises to be rejected

做一些测试,我需要等待几个 promise 被拒绝。

我知道我可以使用 jQuery.when() 来等待几个 promise 被解决。但是一旦其中一个承诺失败,该方法就会拒绝主承诺。

我知道我所有的承诺都会落空,但无论如何我都需要等待。我该怎么做?

在一种伪代码中我想做的是:

var promise1 = connection.doCall();
var promise2 = connection.doCall();

$.when([promise1, promise2]).allOfThemFail(function() {
    assertThatSomeProcessWasDoneOnlyOnce()
});

我认为没有内置函数,但您可以轻松实现

var promise1 = connection.doCall();
var promise2 = connection.doCall();

allOfThemFail([promise1, promise2]).always(function () {
    assertThatSomeProcessWasDoneOnlyOnce()
});


function allOfThemFail(array) {
    var count = 0;
    len = array.length, failed = false, deferred = $.Deferred();

    $.each(array, function (i, item) {
        item.fail(fail).always(always);
    })


    function always() {
        if (++count == len) {
            if (failed) {
                deferred.reject();
            } else {
                deferred.resolve();
            }
        }
    }

    function fail() {
        failed = true;
    }

    return deferred.promise();
}

使用 Bluebird promise 库,您可以使用 Promise.settle() 等待所有 promises 被解决(例如履行或拒绝),然后您可以简单地查询列表以查看它们是否都被拒绝。

var p1 = connection.doCall();
var p2 = connection.doCall();
Promise.settle([p1, p2]).then(function(results) {
    var allRejected = results.every(function(item) {
        return item.isRejected();
    });
    // act on allRejected here
});

这是 settle() 的 jQuery 特定版本:

(function() {    

    function isPromise(p) {
        return p && (typeof p === "object" || typeof p === "function") && typeof p.then === "function";
    }

    function wrapInPromise(p) {
        if (!isPromise(p)) {
            p = $.Deferred().resolve(p);
        }
        return p;
    }

    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, isRejected: function() {
                return !fulfilled;
            }, isPending: function() {
                // PromiseInspection objects created here are never pending
                return false;
            }, value: function() {
                if (!fulfilled) {
                    throw new Error("Can't call .value() on a promise that is not fulfilled");
                }
                return val;
            }, reason: function() {
                if (fulfilled) {
                    throw new Error("Can't call .reason() on a promise that is fulfilled");
                }
                return val;
            }
        };
    }

    // 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);
        }

        return $.when.apply($, args.map(function(p) {
            // make sure p is a promise (it could be just a value)
            p = wrapInPromise(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 by returning a resolved promised
                // One could just return the promiseInspection object directly if jQuery was
                // Promise spec compliant, but jQuery 1.x and 2.x are not so we have to take this extra step
                return wrapInPromise(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);
        });
    }

})();

而且,您可以像使用 Bluebird 解决方案一样使用它:

var p1 = connection.doCall();
var p2 = connection.doCall();
$.settle([p1, p2]).then(function(results) {
    var allRejected = results.every(function(item) {
        return item.isRejected();
    });
    // act on allRejected here
});

而且,这是一个使用标准 ES6 承诺从头开始构建的 settle() 类型函数。它 returns 一个单一的承诺,当所有其他承诺完成时(无论它们的最终状态如何)。这个超级承诺的解决结果是一个 PromiseInspection 对象数组,您可以在其中查询 isFulfilled()isRejected() 并可以获得 .value().reason().

Promise.isPromise = function(p) {
    return p && (typeof p === "object" || typeof p === "function") && typeof p.then === "function";
}

// ES6 version of settle
Promise.settle = function(promises) {
    function PromiseInspection(fulfilled, val) {
        return {
            isFulfilled: function() {
                return fulfilled;
            }, isRejected: function() {
                return !fulfilled;
            }, isPending: function() {
                // PromiseInspection objects created here are never pending
                return false;
            }, value: function() {
                if (!fulfilled) {
                    throw new Error("Can't call .value() on a promise that is not fulfilled");
                }
                return val;
            }, reason: function() {
                if (fulfilled) {
                    throw new Error("Can't call .reason() on a promise that is fulfilled");
                }
                return val;
            }
        };
    }

    return Promise.all(promises.map(function(p) {
        // make sure any values are wrapped in a promise
        if (!Promise.isPromise(p)) {
            p = Promise.resolve(p);
        }
        return p.then(function(val) {
            return new PromiseInspection(true, val);
        }, function(err) {
            return new PromiseInspection(false, val);
        });
    }));
}

并且,您可以像上面一样使用它:

var p1 = connection.doCall();
var p2 = connection.doCall();
Promise.settle([p1, p2]).then(function(results) {
    var allRejected = results.every(function(item) {
        return item.isRejected();
    });
    // act on allRejected here
});