jquery отложенно превращает неудачу в успех

Таким образом, при использовании jQuery deferred и $.when для параллельной загрузки многих объектов.

$.when(
  a.ajax(), b.ajax(), c.ajax()
).then(
  //do something when all are complete
  complete();
);

Теперь b.ajax() иногда будет давать сбои, но меня это не волнует. Я просто хочу дождаться завершения всех вызовов, прежде чем вызывать complete().

К сожалению, как только b терпит неудачу, when() отклоняет и никогда не запускает then()callback. Это AFAIK ожидаемое поведение для $.when(), однако в данном случае меня это не устраивает.

Я действительно хочу сказать:

$.when(
  a.ajax(), b.ajax().fail(return success), c.ajax()
).then(...)

Или, возможно, есть другой способ использования when() или более подходящая конструкция?


person dalyons    schedule 06.06.2011    source источник
comment
@Ates Я думаю, что ваш удаленный ответ можно исправить, просто разрешив этот новый отложенный объект перед вашим возвращением.   -  person Alnitak    schedule 20.06.2011
comment
возможный дубликат $.Deferred: как определить, когда каждое обещание выполнено   -  person Bergi    schedule 14.07.2015


Ответы (4)


Если вы хотите зафиксировать невыполнение обещания и преобразовать его в успех, вы можете использовать failFilter then, чтобы вернуть разрешенное обещание, например:

deferredCall.then(function(answer) { 
   // this is success. you might transform the answer here.
   return transformed;
}, function() {
   // this is a fail. you might resolve the fail with an empty object.
   return $.Deferred().resolve({}).promise();
});

Это гарантирует, что цепочка может продолжаться после сбоя непрерывно.

Итак, для вашего примера вы можете сделать это:

$.when([
   a.ajax(),
   b.ajax().then(function(answer) { 
       return answer; 
   }, function() {
       return $.Deferred().resolve({}).promise();
   }),
   c.ajax()
]).then(function(results) {
    // etc.
});

Пример 2. В своих приложениях я иногда использую then, чтобы получить реляционные данные для определенного объекта и разрешить возможность 404, чтобы указать, что такой связи не существует:

getEntity(id).then(function(entity) {
    return getAssociation(id).then(function(association) {
        entity.association = association;
        return entity;
    }, function() {
        entity.association = null;
        return $.Deferred().resolve(entity).promise();
    });
}).done(function(entity) {
    // etc.
});

Обратите внимание, что в более старых ответах предлагается использовать метод pipe. Этот метод устарел, начиная с jQuery 1.8.

person Griffin    schedule 13.05.2015
comment
Любые идеи, почему это не сработает? Я пробовал, и это все равно приводит к сбою. - person Andrey; 14.01.2016
comment
Важно, чтобы ваш обработчик сбоев (вторая функция) возвращал разрешенное обещание. Неудачное обещание или любое значение, не являющееся обещанием, позволит продолжить цепочку до другого обработчика отказа. - person Griffin; 14.01.2016

Вот кое-что получше, чем превращать неудачу в успех.

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

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

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

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

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

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

person InfinitiesLoop    schedule 24.10.2011

Вы можете довольно легко построить $.onFailSucceed, обернув объект $.Deferred:

$.onCompleteSucceed = function(oldDfd) {
    var newDfd = $.Deferred();

    oldDfd.always(newDfd.resolve);

    return newDfd.promise();
}

Затем вы можете обернуть соответствующие вызовы в этот метод:

$.when(
  a.ajax(), $.onCompleteSucceed(b.ajax()), c.ajax()
).then(...)
person lonesomeday    schedule 07.06.2011
comment
хорошее решение, за исключением того, что оно не передает результат b.ajax() в обработчик .then(). - person Alnitak; 20.06.2011

Итак, в конце концов я понял это, посмотрите мой ответ кому-то еще с той же проблемой:

как обмануть jqXHR, чтобы добиться успеха всегда

Ответ lonesomeday был аккуратным, но не совсем тем, что мне было нужно.

person dalyons    schedule 28.06.2011
comment
Начиная с jQuery 1.8, канал устарел. См. рецензию в разделе jQuery Deferred then. - person Griffin; 13.05.2015