Jquery отложен - "всегда", вызванный при первом отказе

Я использую $.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.

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

Ответ 1

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

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/

Ответ 2

Да, это нормальное поведение. Если кто-то терпит неудачу, то и то, что полагается на все, терпит неудачу. См. Также jQuery docs.

Итак, вам либо нужно отследить их вручную, либо вы только разрешили Promises в 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, когда обрабатываются все Promises, и он получит массив объектов, указывающий статус и результат их.