jquery deferred - “always” called at the first rej

2019-04-21 06:12发布

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?

2条回答
We Are One
2楼-- · 2019-04-21 06:19

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.

查看更多
Summer. ? 凉城
3楼-- · 2019-04-21 06:37

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/

查看更多
登录 后发表回答