blob: 59a1587c1065a139d9a91f4accf2f1225117417d [file] [log] [blame]
// Copyright 2005 Bob Ippolito. All Rights Reserved.
// Modifications Copyright 2009 The Closure Library Authors.
// All Rights Reserved.
/**
* Portions of this code are from MochiKit, received by The Closure
* Library Authors under the MIT license. All other code is Copyright
* 2005-2009 The Closure Library Authors. All Rights Reserved.
*/
/**
* @fileoverview Class for tracking multiple asynchronous operations and
* handling the results. The DeferredList object here is patterned after the
* DeferredList object in the Twisted python networking framework.
*
* Based on the MochiKit code.
*
* See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
*
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('goog.async.DeferredList');
goog.require('goog.async.Deferred');
/**
* Constructs an object that waits on the results of multiple asynchronous
* operations and marshals the results. It is itself a <code>Deferred</code>,
* and may have an execution sequence of callback functions added to it. Each
* <code>DeferredList</code> instance is single use and may be fired only once.
*
* The default behavior of a <code>DeferredList</code> is to wait for a success
* or error result from every <code>Deferred</code> in its input list. Once
* every result is available, the <code>DeferredList</code>'s execution sequence
* is fired with a list of <code>[success, result]</code> array pairs, where
* <code>success</code> is a boolean indicating whether <code>result</code> was
* the product of a callback or errback. The list's completion criteria and
* result list may be modified by setting one or more of the boolean options
* documented below.
*
* <code>Deferred</code> instances passed into a <code>DeferredList</code> are
* independent, and may have additional callbacks and errbacks added to their
* execution sequences after they are passed as inputs to the list.
*
* @param {!Array<!goog.async.Deferred>} list An array of deferred results to
* wait for.
* @param {boolean=} opt_fireOnOneCallback Whether to stop waiting as soon as
* one input completes successfully. In this case, the
* <code>DeferredList</code>'s callback chain will be called with a two
* element array, <code>[index, result]</code>, where <code>index</code>
* identifies which input <code>Deferred</code> produced the successful
* <code>result</code>.
* @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one
* input reports an error. The failing result is passed to the
* <code>DeferredList</code>'s errback sequence.
* @param {boolean=} opt_consumeErrors When true, any errors fired by a
* <code>Deferred</code> in the input list will be captured and replaced
* with a succeeding null result. Any callbacks added to the
* <code>Deferred</code> after its use in the <code>DeferredList</code> will
* receive null instead of the error.
* @param {Function=} opt_canceler A function that will be called if the
* <code>DeferredList</code> is canceled. @see goog.async.Deferred#cancel
* @param {Object=} opt_defaultScope The default scope to invoke callbacks or
* errbacks in.
* @constructor
* @extends {goog.async.Deferred}
*/
goog.async.DeferredList = function(
list, opt_fireOnOneCallback, opt_fireOnOneErrback, opt_consumeErrors,
opt_canceler, opt_defaultScope) {
goog.async.DeferredList.base(this, 'constructor',
opt_canceler, opt_defaultScope);
/**
* The list of Deferred objects to wait for.
* @const {!Array<!goog.async.Deferred>}
* @private
*/
this.list_ = list;
/**
* The stored return values of the Deferred objects.
* @const {!Array}
* @private
*/
this.deferredResults_ = [];
/**
* Whether to fire on the first successful callback instead of waiting for
* every Deferred to complete.
* @const {boolean}
* @private
*/
this.fireOnOneCallback_ = !!opt_fireOnOneCallback;
/**
* Whether to fire on the first error result received instead of waiting for
* every Deferred to complete.
* @const {boolean}
* @private
*/
this.fireOnOneErrback_ = !!opt_fireOnOneErrback;
/**
* Whether to stop error propagation on the input Deferred objects. If the
* DeferredList sees an error from one of the Deferred inputs, the error will
* be captured, and the Deferred will be returned to success state with a null
* return value.
* @const {boolean}
* @private
*/
this.consumeErrors_ = !!opt_consumeErrors;
/**
* The number of input deferred objects that have fired.
* @private {number}
*/
this.numFinished_ = 0;
for (var i = 0; i < list.length; i++) {
var d = list[i];
d.addCallbacks(goog.bind(this.handleCallback_, this, i, true),
goog.bind(this.handleCallback_, this, i, false));
}
if (list.length == 0 && !this.fireOnOneCallback_) {
this.callback(this.deferredResults_);
}
};
goog.inherits(goog.async.DeferredList, goog.async.Deferred);
/**
* Registers the result from an input deferred callback or errback. The result
* is returned and may be passed to additional handlers in the callback chain.
*
* @param {number} index The index of the firing deferred object in the input
* list.
* @param {boolean} success Whether the result is from a callback or errback.
* @param {*} result The result of the callback or errback.
* @return {*} The result, to be handled by the next handler in the deferred's
* callback chain (if any). If consumeErrors is set, an error result is
* replaced with null.
* @private
*/
goog.async.DeferredList.prototype.handleCallback_ = function(
index, success, result) {
this.numFinished_++;
this.deferredResults_[index] = [success, result];
if (!this.hasFired()) {
if (this.fireOnOneCallback_ && success) {
this.callback([index, result]);
} else if (this.fireOnOneErrback_ && !success) {
this.errback(result);
} else if (this.numFinished_ == this.list_.length) {
this.callback(this.deferredResults_);
}
}
if (this.consumeErrors_ && !success) {
result = null;
}
return result;
};
/** @override */
goog.async.DeferredList.prototype.errback = function(res) {
goog.async.DeferredList.base(this, 'errback', res);
// On error, cancel any pending requests.
for (var i = 0; i < this.list_.length; i++) {
this.list_[i].cancel();
}
};
/**
* Creates a <code>DeferredList</code> that gathers results from multiple
* <code>Deferred</code> inputs. If all inputs succeed, the callback is fired
* with the list of results as a flat array. If any input fails, the list's
* errback is fired immediately with the offending error, and all other pending
* inputs are canceled.
*
* @param {!Array<!goog.async.Deferred>} list The list of <code>Deferred</code>
* inputs to wait for.
* @return {!goog.async.Deferred} The deferred list of results from the inputs
* if they all succeed, or the error result of the first input to fail.
*/
goog.async.DeferredList.gatherResults = function(list) {
return new goog.async.DeferredList(list, false, true).
addCallback(function(results) {
var output = [];
for (var i = 0; i < results.length; i++) {
output[i] = results[i][1];
}
return output;
});
};