jquery deferred - “always” called at the first reject

人盡茶涼 提交于 2019-12-22 03:48:29

问题


I'm using $.when to chain some Deferred objects, and if one of them fail, the always method will be called directly after the failure, even if I still have some deferrer in a "pending" state.

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); 

Check this jsfiddle.

Maybe it's the normal behavior. But, in this case, how can I catch the end of my chain even if some of them have failed?


回答1:


It's by design: The method will resolve its master Deferred as soon as all the Deferreds resolve, or reject the master Deferred as soon as one of the Deferreds is rejected. [...] Note that some of the Deferreds may still be unresolved at that point.

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

You can save references to all deferreds and track them separately.

Something like this:

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:


Yes, it is normal behavior. If one fails, the thing that relies on all fails as well. See also the jQuery docs.

So, you either have to track them manually, or you feed only resolved Promises into when:

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

With this, your res will only call the done callback when all promises are processed, and it will get an array of objects indicating status and result of them.



来源:https://stackoverflow.com/questions/13493084/jquery-deferred-always-called-at-the-first-reject

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!