While the $.Deferred.when
function will fire immediately if one of your promisses fails, you can create a 'wrapper' Deferred to handle an array of promisses and dispatch them as they come in then fire the master's own handlers once all are complete, even if some fail and some succeed. This particular function is just copied from a all-purpose tools file I have and uses Underscore for brevity, but the essential pattern is what you need:
function completed( firstParam ) {
var args = _.toArray( arguments ),
i = 0,
length = args.length,
pValues = new Array( length ),
count = length,
deferred = length <= 1 && firstParam && $.isFunction( firstParam.promise ) ? firstParam : $.Deferred(),
promise = deferred.promise(),
state = 'resolved';
function alwaysFunc( i ) {
return function ( value ) {
args[ i ] = arguments.length > 1 ? _.toArray( arguments ) : value;
state = ( this.state && this.state() === 'rejected' ) ? 'rejected' : state;
if ( !( --count ) ) deferred[ ( state === 'rejected' ? 'reject' : 'resolve' ) + 'With' ]( deferred, args );
};
}
function progressFunc( i ) {
return function ( value ) {
pValues[ i ] = arguments.length > 1 ? _.toArray( arguments ) : value;
deferred.notifyWith( promise, pValues );
};
}
if ( length > 1 ) {
for ( ; i < length; i++ ) {
if ( args[ i ] && args[ i ].promise && $.isFunction( args[ i ].promise ) )
args[ i ].promise().always( alwaysFunc( i ) ).progress( progressFunc( i ) );
else --count;
}
if ( !count ) deferred.resolveWith( deferred, args );
} else if ( deferred !== firstParam ) deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
return promise;
}
Anyway, you create and attach handlers to individual requests/promisses as usual, then pass them all through this function via apply
. Each request's state is handled separately and the function keeps track of how many are still unresolved. Once all are resolved, it fires it's own resolution based on the collection. Even if one or all of the component promisses fail, all are still executed and the master Deferred waits till they're all resolved. It doesn't handle adding further promisses/deferreds after the initial call - create all the one's you need, then pass them to this function.
I can't take credit for the script: it was 'passed down' to me via a coworker - whom I assume got it from some place else but was the worst person in the world for keeping comments/attributions in code. If anyone recognizes the code and can point me in the direction of the author...