// 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 Deferred, * and may have an execution sequence of callback functions added to it. Each * DeferredList instance is single use and may be fired only once. * * The default behavior of a DeferredList is to wait for a success * or error result from every Deferred in its input list. Once * every result is available, the DeferredList's execution sequence * is fired with a list of [success, result] array pairs, where * success is a boolean indicating whether result 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. * * Deferred instances passed into a DeferredList 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} 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 * DeferredList's callback chain will be called with a two * element array, [index, result], where index * identifies which input Deferred produced the successful * result. * @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one * input reports an error. The failing result is passed to the * DeferredList's errback sequence. * @param {boolean=} opt_consumeErrors When true, any errors fired by a * Deferred in the input list will be captured and replaced * with a succeeding null result. Any callbacks added to the * Deferred after its use in the DeferredList will * receive null instead of the error. * @param {Function=} opt_canceler A function that will be called if the * DeferredList 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} * @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 DeferredList that gathers results from multiple * Deferred 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} list The list of Deferred * 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; }); };