Подождите, пока все не выполнит даже сбои, используя $.when()

С jQuery.when(), если один запрос терпит неудачу, то все они терпят неудачу.

$.when(deferred1, deferred2).then(function() {
    // Called only after both deferred are resolved
}, function() {
    // Called immediately after one of the deferreds is rejected
});

Как можно ждать, пока все исполнится, даже неудачи? Я пытался использовать .always() вместо .then(), но обратный вызов также вызывается сразу после первого сбоя.

Одним (уродливым) решением было бы иметь флаги для управления, когда обе операции завершены, и использовать .always() для каждой из них (без $.when()).

var deferred1Complete = false;
var deferred2Complete = false;

function callback() {
    if (deferred1Complete && deferred2Complete) {
        // Execute after both deferred are either resolved or rejected
    }
}

deferred1.always(function() {
    deferred1Complete = true;
    callback();
});

deferred2.always(function() {
    deferred2Complete = true;
    callback();
});

Есть лучший способ сделать это?


person Jonatan    schedule 09.03.2013    source источник
comment
Я нашел другие интересные решения в этих вопросах (stackoverflow.com/questions/12152595/) и (stackoverflow.com/ вопросы/5824615/)   -  person Jonatan    schedule 09.03.2013


Ответы (2)


Джонатан,

Мне нравится ваше решение, и я предлагаю несколько улучшений:

  • фраза как служебный метод jQuery, например $.when()
  • разрешить передачу отдельных промисов или массива промисов в
  • разрешить немедленно, если аргументы не переданы или пустой массив передан
  • быть терпимым к не обещаниям/не отсрочкам
  • разрешить отчет о ходе цепочки, когда каждое обещание разрешено или отклонено.

Вот код:

(function($) {
    $.whenAlways = function(chain) {
        if(!chain || !$.isArray(chain)) chain = $.extend([], arguments);
        var n = chain.length;
        return $.Deferred(function(deferred) {
            $.each(chain, function(i, promise) {
                if(!promise.always) n--;
                else {
                    promise.always(function() {
                        deferred.notify(--n);
                        if(n == 0) deferred.resolve();
                    });
                }
            });
            if(n == 0) deferred.resolve();
        }).promise();
    }
})(jQuery);

Этот подход позволит вызывать любые progressCallbacks (добавленные к перенастроенному обещанию) всякий раз, когда обещание chain пересматривается/отклоняется, независимо от порядка разрешения/отклонения. Передавая количество невыполненных обещаний chain в progressCallbacks, вы могли бы, например, предоставить индикацию обратного отсчета или ответить на промежуточные вехи обратного отсчета.

Кажется, работает - DEMO

person Beetroot-Beetroot    schedule 09.03.2013

Другим решением (не таким уродливым) было бы объединение в цепочку обратных вызовов deferreds .always().

deferred1.always(function() {
    deferred2.always(function() {
        // Execute after both deferred are either resolved or rejected
    });
});

Я написал небольшую функцию для обработки этого:

function whenAlways() {
    var chain = $.extend([], arguments);
    return new $.Deferred(function(deferred) {
        var callback = function() {
            if (chain.length == 0) {
                deferred.resolve();
                return;
            }
            var object = chain.shift();
            $.when(object).always(callback);
        };
        callback();
    }).promise();
}

Применение:

whenAlways(deferred1, deferred2)
    .done(function() {
        // Execute after both deferred are either resolved or rejected
    });
person Jonatan    schedule 09.03.2013