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?
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/
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.