JQuery Deferred, $.when() и аргументы обратного вызова fail()

Я получаю неожиданный результат при использовании $.when(), когда одна из отложенных операций не выполняется.

Возьмите этот JavaScript, который создал 2 отложенных. Первый преуспевает, а второй терпит неудачу.

var f1 = function() {
    return $.Deferred(function(dfd) {
        dfd.resolve('123 from f1');
    }).promise();
};

var f2 = function() {
    return $.Deferred(function(dfd) {
        dfd.reject('456 from f2');
    }).promise();
};

$.when(f1(), f2())
    .then(function(f1Val, f2Val) {
        alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    })
    .fail(function(f1Val, f2Val) {
        alert('fail!    f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    });

Запустите его самостоятельно: http://jsfiddle.net/r2d3j/2/

Я получаю fail! f1, f2: ["456 from f2", null]

Проблема заключается в том, что в обратном вызове .fail() значение, переданное с отклонением f2(), направляется к первому аргументу, где я ожидаю f1Value. Это означает, что я действительно не знаю, какой отложенный объект фактически опубликовал, что reject(), и я также не знаю, к какой операции относятся данные отказа.

Я бы ожидал, что .fail() получит аргументы null, '456 from f2', так как первая отсрочка не потерпит неудачу. Или я просто не делаю отложенных прав здесь?

Как узнать, какие отложенные неудачи, и какие аргументы отклонения принадлежат, которые не были отложены, если порядок аргументов в обратном вызове не соблюдается?

Ответ 1

Внутренне пути "отклонить" и "сбой" обрабатываются двумя полностью отдельными очередями, поэтому он просто не работает так, как вы ожидаете.

Чтобы узнать, какой исходный Deferred не удалось выполнить из группы "when()", вы могли бы передать их вместе с вызовом ".reject() как часть литерала объекта или что-то в этом роде.

Ответ 2

$.when() немедленно выполнит неудачный обратный вызов (второй параметр, переданный в then()), если какой-либо из параметров завершится с ошибкой. Это по дизайну. Чтобы процитировать документацию:

http://api.jquery.com/jQuery.when/

В случае с несколькими отсрочками, когда одна из Отложенных отклонена, jQuery.when немедленно запускает failCallbacks для своего хозяина Deferred. Обратите внимание, что некоторые из Отсрочек могут все еще оставаться нерешенными в этот момент. Если вам необходимо выполнить дополнительную обработку для этого случая, например, отменить любые незавершенные запросы ajax, вы можете сохранить ссылки на лежащие в основе объекты jqXHR в закрытии и проверить/отменить их в failCallback.

На самом деле нет встроенного способа получения обратного вызова, который ждет, пока все они не будут завершены независимо от их статуса успеха/отказа.

Итак, я построил для вас $.whenAll():)
Он всегда ждет, пока все они не разрешат так или иначе:

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

$.whenAll(a, b, c)
    .then( callbackUponAllResolvedOrRejected );

Ответ 3

Я столкнулся с этой же проблемой, и я справился с ней, используя обратный вызов .allways и проверив мой массив отложенных объектов. У меня было неизвестное количество вызовов ajax, поэтому мне пришлось сделать следующее:

// array of ajax deletes
var deletes = [];
$checkboxes.each(function () {
    deletes.push(deleteFile(this));
});

$.when.apply($, deletes)
  .always(function () {
      // unfortunately .fail shortcircuits and returns the first fail,
      // so we have to loop the deferred objects and see what happened.

      $.each(deletes, function () {
          this.done(function () {
              console.log("done");
          }).fail(function () {
              console.log("fail");
          });
      });
  });

Метод deleteFile возвращает обещание, которое имеет обратные вызовы .done или .file.

Это позволяет вам действовать после завершения всех отложенных событий. В моем случае я собираюсь показать сводку ошибки файла удаления.

Я просто попробовал это, и, к сожалению, мне пришлось установить таймер с интервалом, чтобы проверить, что все они действительно выполняются после моего $.each на отложенных объектах. Это кажется странным и противоречивым.

Все еще пытаясь понять эти отсрочки!

Ответ 4

http://jsfiddle.net/InfinitiesLoop/yQsYK/

Это всегда будет отклоняться, если задано несколько входов. rejected = true; должен быть rejected |= reject;