jquery deferred - всегда вызывается при первом отклонении

Я использую $.when для объединения некоторых отложенных объектов, и если один из них выйдет из строя, метод always будет вызван сразу после сбоя, даже если у меня все еще есть какой-то отложенный объект в состоянии "ожидание".

var promises = [], defs = [];

for(var i=0 ; i < 10 ; i++){
    defs.push($.Deferred());
    promises.push(defs[i].promise());
}

var res = $.when.apply($, promises);

res.fail(function(){console.log('failed')});
res.done(function(){console.log('done')});
res.always(function(){console.log('always')});
res.then(function(){console.log('then, done')},      
         function(){console.log('then, failed')});        

var j = 0;                      
var t = setInterval(function(){
    if(j < 10){
        if(j < 5) {
            console.log('resolve');
            defs[j++].resolve();    
        }
        else {
            console.log('reject');
            defs[j++].reject();
        }
    }
    else {
        clearInterval(t);        
    }
}, 200); 

Проверьте этот jsfiddle.

Возможно, это нормальное поведение. Но в таком случае, как мне поймать конец моей цепочки, даже если некоторые из них вышли из строя?


person zazabe    schedule 21.11.2012    source источник


Ответы (2)


Так задумано: метод разрешит свой основной Deferred, как только разрешатся все Deferred, или отклонит главный Deferred, как только будет отклонен один из Deferred. [...] Обратите внимание, что некоторые из отсроченных запросов могут все еще быть неразрешенными в этот момент.

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

Вы можете сохранять ссылки на все отложенные и отслеживать их отдельно.

Что-то вроде этого:

var whenAll = function() {
   var dfd = $.Deferred(),
       len = arguments.length,
       counter = 0,
       state = "resolved",
       resolveOrReject = function() {
           if(this.state() === "rejected"){
               state = "rejected";
           }
           counter++;

           if(counter === len) {
               dfd[state === "rejected"? "reject": "resolve"]();   
           }

       };


    $.each(arguments, function(idx, item) {
        item.always(resolveOrReject); 
    });

   return dfd.promise();    
};

http://jsfiddle.net/cSy2K/2/

person Yury Tarabanko    schedule 21.11.2012
comment
спасибо @yury-tarabanko ! Мне нравится идея расширить jQuery и добавить многоразовый метод для просмотра всех отложенных окончаний, даже если они потерпели неудачу. Основываясь на вашей идее и на том, когда источник jQuery, я снова сделал whenAll, проверьте источник, если у вас есть время jsfiddle. сеть/cSy2K/5 ;) - person zazabe; 22.11.2012

Да, это нормальное поведение. Если один терпит неудачу, вещь, которая зависит от всех, также терпит неудачу. См. также документацию по jQuery.

Таким образом, вы либо должны отслеживать их вручную, либо вы передаете только разрешенные промисы в when:

promises.push( defs[i].promise().then(function(x) {
      return {result:x,resolved:true};
  }, function(x) {
      return (new $.Deferred).resolve({result:x,resolved:false});
  })
);

При этом ваш res будет вызывать обратный вызов done только тогда, когда все промисы будут обработаны, и он получит массив объектов, указывающих их статус и результат.

person Bergi    schedule 21.11.2012