JQuery $.wait() для * all * отложен для отклонения?

jQuery имеет приятную функцию своего API Deferred, $.wait() для работы с несколькими Deferred/Promise s. Он возвращается, когда:

  • Все Deferred были resolve() d

или

  • Один Deferred был reject() ed

В большинстве случаев это то, что вы хотите, но иногда вы хотите знать, когда все из них были reject() ed.

Есть ли простой или элегантный способ сделать что-то вроде $.wait(), но только когда все Deferred были отклонены?

(Могут быть и другие варианты использования, но мой должен объединить это с ожидая, когда первая из нескольких Отложенных будет решена.)

Ответ 1

В духе того, как спецификация Promise, скорее всего, собирается на будущее с объектом PromiseInspection, здесь добавлена ​​функция добавления jQuery, которая сообщает вам, когда все promises выполнены, независимо от того, выполнены или отклонены:

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