Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions
@@ -0,0 +1,764 @@
// Copyright 2007 Bob Ippolito. All Rights Reserved.
// Modifications Copyright 2009 The Closure Library Authors. All Rights
// Reserved.
/**
* @license Portions of this code are from MochiKit, received by
* The Closure Authors under the MIT license. All other code is Copyright
* 2005-2009 The Closure Authors. All Rights Reserved.
*/
/**
* @fileoverview Classes for tracking asynchronous operations and handling the
* results. The Deferred object here is patterned after the Deferred object in
* the Twisted python networking framework.
*
* See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
*
* Based on the Dojo code which in turn is based on the MochiKit code.
*
*/
goog.provide('goog.async.Deferred');
goog.provide('goog.async.Deferred.AlreadyCalledError');
goog.provide('goog.async.Deferred.CanceledError');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.debug.Error');
goog.require('goog.functions');
/**
* A Deferred represents the result of an asynchronous operation. A Deferred
* instance has no result when it is created, and is "fired" (given an initial
* result) by calling {@code callback} or {@code errback}.
*
* Once fired, the result is passed through a sequence of callback functions
* registered with {@code addCallback} or {@code addErrback}. The functions may
* mutate the result before it is passed to the next function in the sequence.
*
* Callbacks and errbacks may be added at any time, including after the Deferred
* has been "fired". If there are no pending actions in the execution sequence
* of a fired Deferred, any new callback functions will be called with the last
* computed result. Adding a callback function is the only way to access the
* result of the Deferred.
*
* If a Deferred operation is canceled, an optional user-provided cancellation
* function is invoked which may perform any special cleanup, followed by firing
* the Deferred's errback sequence with a {@code CanceledError}. If the
* Deferred has already fired, cancellation is ignored.
*
* @param {Function=} opt_onCancelFunction A function that will be called if the
* Deferred is canceled. If provided, this function runs before the
* Deferred is fired with a {@code CanceledError}.
* @param {Object=} opt_defaultScope The default object context to call
* callbacks and errbacks in.
* @constructor
*/
goog.async.Deferred = function(opt_onCancelFunction, opt_defaultScope) {
/**
* Entries in the sequence are arrays containing a callback, an errback, and
* an optional scope. The callback or errback in an entry may be null.
* @type {!Array.<!Array>}
* @private
*/
this.sequence_ = [];
/**
* Optional function that will be called if the Deferred is canceled.
* @type {Function|undefined}
* @private
*/
this.onCancelFunction_ = opt_onCancelFunction;
/**
* The default scope to execute callbacks and errbacks in.
* @type {Object}
* @private
*/
this.defaultScope_ = opt_defaultScope || null;
if (goog.async.Deferred.LONG_STACK_TRACES) {
/**
* Holds the stack trace at time of deferred creation if the JS engine
* provides the Error.captureStackTrace API.
* @private {?string}
*/
this.constructorStack_ = null;
if (Error.captureStackTrace) {
var target = { stack: '' };
Error.captureStackTrace(target, goog.async.Deferred);
// Check if Error.captureStackTrace worked. It fails in gjstest.
if (typeof target.stack == 'string') {
// Remove first line and force stringify to prevent memory leak due to
// holding on to actual stack frames.
this.constructorStack_ = target.stack.replace(/^[^\n]*\n/, '');
}
}
}
};
/**
* Whether the Deferred has been fired.
* @type {boolean}
* @private
*/
goog.async.Deferred.prototype.fired_ = false;
/**
* Whether the last result in the execution sequence was an error.
* @type {boolean}
* @private
*/
goog.async.Deferred.prototype.hadError_ = false;
/**
* The current Deferred result, updated as callbacks and errbacks are executed.
* @type {*}
* @private
*/
goog.async.Deferred.prototype.result_;
/**
* Whether the Deferred is blocked waiting on another Deferred to fire. If a
* callback or errback returns a Deferred as a result, the execution sequence is
* blocked until that Deferred result becomes available.
* @type {boolean}
* @private
*/
goog.async.Deferred.prototype.blocked_ = false;
/**
* Whether this Deferred is blocking execution of another Deferred. If this
* instance was returned as a result in another Deferred's execution sequence,
* that other Deferred becomes blocked until this instance's execution sequence
* completes. No additional callbacks may be added to a Deferred once it
* is blocking another instance.
* @type {boolean}
* @private
*/
goog.async.Deferred.prototype.blocking_ = false;
/**
* Whether the Deferred has been canceled without having a custom cancel
* function.
* @type {boolean}
* @private
*/
goog.async.Deferred.prototype.silentlyCanceled_ = false;
/**
* If an error is thrown during Deferred execution with no errback to catch it,
* the error is rethrown after a timeout. Reporting the error after a timeout
* allows execution to continue in the calling context.
* @type {number}
* @private
*/
goog.async.Deferred.prototype.unhandledExceptionTimeoutId_;
/**
* If this Deferred was created by branch(), this will be the "parent" Deferred.
* @type {goog.async.Deferred}
* @private
*/
goog.async.Deferred.prototype.parent_;
/**
* The number of Deferred objects that have been branched off this one. This
* will be decremented whenever a branch is fired or canceled.
* @type {number}
* @private
*/
goog.async.Deferred.prototype.branches_ = 0;
/**
* @define {boolean} Whether unhandled errors should always get rethrown to the
* global scope. Defaults to the value of goog.DEBUG.
*/
goog.define('goog.async.Deferred.STRICT_ERRORS', false);
/**
* @define {boolean} Whether to attempt to make stack traces long. Defaults to
* the value of goog.DEBUG.
*/
goog.define('goog.async.Deferred.LONG_STACK_TRACES', goog.DEBUG);
/**
* Cancels a Deferred that has not yet been fired, or is blocked on another
* deferred operation. If this Deferred is waiting for a blocking Deferred to
* fire, the blocking Deferred will also be canceled.
*
* If this Deferred was created by calling branch() on a parent Deferred with
* opt_propagateCancel set to true, the parent may also be canceled. If
* opt_deepCancel is set, cancel() will be called on the parent (as well as any
* other ancestors if the parent is also a branch). If one or more branches were
* created with opt_propagateCancel set to true, the parent will be canceled if
* cancel() is called on all of those branches.
*
* @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even
* if cancel() hasn't been called on some of the parent's branches. Has no
* effect on a branch without opt_propagateCancel set to true.
*/
goog.async.Deferred.prototype.cancel = function(opt_deepCancel) {
if (!this.hasFired()) {
if (this.parent_) {
// Get rid of the parent reference before potentially running the parent's
// canceler function to ensure that this cancellation isn't
// double-counted.
var parent = this.parent_;
delete this.parent_;
if (opt_deepCancel) {
parent.cancel(opt_deepCancel);
} else {
parent.branchCancel_();
}
}
if (this.onCancelFunction_) {
// Call in user-specified scope.
this.onCancelFunction_.call(this.defaultScope_, this);
} else {
this.silentlyCanceled_ = true;
}
if (!this.hasFired()) {
this.errback(new goog.async.Deferred.CanceledError(this));
}
} else if (this.result_ instanceof goog.async.Deferred) {
this.result_.cancel();
}
};
/**
* Handle a single branch being canceled. Once all branches are canceled, this
* Deferred will be canceled as well.
*
* @private
*/
goog.async.Deferred.prototype.branchCancel_ = function() {
this.branches_--;
if (this.branches_ <= 0) {
this.cancel();
}
};
/**
* Called after a blocking Deferred fires. Unblocks this Deferred and resumes
* its execution sequence.
*
* @param {boolean} isSuccess Whether the result is a success or an error.
* @param {*} res The result of the blocking Deferred.
* @private
*/
goog.async.Deferred.prototype.continue_ = function(isSuccess, res) {
this.blocked_ = false;
this.updateResult_(isSuccess, res);
};
/**
* Updates the current result based on the success or failure of the last action
* in the execution sequence.
*
* @param {boolean} isSuccess Whether the new result is a success or an error.
* @param {*} res The result.
* @private
*/
goog.async.Deferred.prototype.updateResult_ = function(isSuccess, res) {
this.fired_ = true;
this.result_ = res;
this.hadError_ = !isSuccess;
this.fire_();
};
/**
* Verifies that the Deferred has not yet been fired.
*
* @private
* @throws {Error} If this has already been fired.
*/
goog.async.Deferred.prototype.check_ = function() {
if (this.hasFired()) {
if (!this.silentlyCanceled_) {
throw new goog.async.Deferred.AlreadyCalledError(this);
}
this.silentlyCanceled_ = false;
}
};
/**
* Fire the execution sequence for this Deferred by passing the starting result
* to the first registered callback.
* @param {*=} opt_result The starting result.
*/
goog.async.Deferred.prototype.callback = function(opt_result) {
this.check_();
this.assertNotDeferred_(opt_result);
this.updateResult_(true /* isSuccess */, opt_result);
};
/**
* Fire the execution sequence for this Deferred by passing the starting error
* result to the first registered errback.
* @param {*=} opt_result The starting error.
*/
goog.async.Deferred.prototype.errback = function(opt_result) {
this.check_();
this.assertNotDeferred_(opt_result);
this.makeStackTraceLong_(opt_result);
this.updateResult_(false /* isSuccess */, opt_result);
};
/**
* Attempt to make the error's stack trace be long in that it contains the
* stack trace from the point where the deferred was created on top of the
* current stack trace to give additional context.
* @param {*} error
* @private
*/
goog.async.Deferred.prototype.makeStackTraceLong_ = function(error) {
if (!goog.async.Deferred.LONG_STACK_TRACES) {
return;
}
if (this.constructorStack_ && goog.isObject(error) && error.stack &&
// Stack looks like it was system generated. See
// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
(/^[^\n]+(\n [^\n]+)+/).test(error.stack)) {
error.stack = error.stack + '\nDEFERRED OPERATION:\n' +
this.constructorStack_;
}
};
/**
* Asserts that an object is not a Deferred.
* @param {*} obj The object to test.
* @throws {Error} Throws an exception if the object is a Deferred.
* @private
*/
goog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {
goog.asserts.assert(
!(obj instanceof goog.async.Deferred),
'An execution sequence may not be initiated with a blocking Deferred.');
};
/**
* Register a callback function to be called with a successful result. If no
* value is returned by the callback function, the result value is unchanged. If
* a new value is returned, it becomes the Deferred result and will be passed to
* the next callback in the execution sequence.
*
* If the function throws an error, the error becomes the new result and will be
* passed to the next errback in the execution chain.
*
* If the function returns a Deferred, the execution sequence will be blocked
* until that Deferred fires. Its result will be passed to the next callback (or
* errback if it is an error result) in this Deferred's execution sequence.
*
* @param {!function(this:T,?):?} cb The function to be called with a successful
* result.
* @param {T=} opt_scope An optional scope to call the callback in.
* @return {!goog.async.Deferred} This Deferred.
* @template T
*/
goog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {
return this.addCallbacks(cb, null, opt_scope);
};
/**
* Register a callback function to be called with an error result. If no value
* is returned by the function, the error result is unchanged. If a new error
* value is returned or thrown, that error becomes the Deferred result and will
* be passed to the next errback in the execution sequence.
*
* If the errback function handles the error by returning a non-error value,
* that result will be passed to the next normal callback in the sequence.
*
* If the function returns a Deferred, the execution sequence will be blocked
* until that Deferred fires. Its result will be passed to the next callback (or
* errback if it is an error result) in this Deferred's execution sequence.
*
* @param {!function(this:T,?):?} eb The function to be called on an
* unsuccessful result.
* @param {T=} opt_scope An optional scope to call the errback in.
* @return {!goog.async.Deferred} This Deferred.
* @template T
*/
goog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {
return this.addCallbacks(null, eb, opt_scope);
};
/**
* Registers one function as both a callback and errback.
*
* @param {!function(this:T,?):?} f The function to be called on any result.
* @param {T=} opt_scope An optional scope to call the function in.
* @return {!goog.async.Deferred} This Deferred.
* @template T
*/
goog.async.Deferred.prototype.addBoth = function(f, opt_scope) {
return this.addCallbacks(f, f, opt_scope);
};
/**
* Registers a callback function and an errback function at the same position
* in the execution sequence. Only one of these functions will execute,
* depending on the error state during the execution sequence.
*
* NOTE: This is not equivalent to {@code def.addCallback().addErrback()}! If
* the callback is invoked, the errback will be skipped, and vice versa.
*
* @param {(function(this:T,?):?)|null} cb The function to be called on a
* successful result.
* @param {(function(this:T,?):?)|null} eb The function to be called on an
* unsuccessful result.
* @param {T=} opt_scope An optional scope to call the functions in.
* @return {!goog.async.Deferred} This Deferred.
* @template T
*/
goog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {
goog.asserts.assert(!this.blocking_, 'Blocking Deferreds can not be re-used');
this.sequence_.push([cb, eb, opt_scope]);
if (this.hasFired()) {
this.fire_();
}
return this;
};
/**
* Links another Deferred to the end of this Deferred's execution sequence. The
* result of this execution sequence will be passed as the starting result for
* the chained Deferred, invoking either its first callback or errback.
*
* @param {!goog.async.Deferred} otherDeferred The Deferred to chain.
* @return {!goog.async.Deferred} This Deferred.
*/
goog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {
this.addCallbacks(
otherDeferred.callback, otherDeferred.errback, otherDeferred);
return this;
};
/**
* Makes this Deferred wait for another Deferred's execution sequence to
* complete before continuing.
*
* This is equivalent to adding a callback that returns {@code otherDeferred},
* but doesn't prevent additional callbacks from being added to
* {@code otherDeferred}.
*
* @param {!goog.async.Deferred} otherDeferred The Deferred to wait for.
* @return {!goog.async.Deferred} This Deferred.
*/
goog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {
return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));
};
/**
* Creates a branch off this Deferred's execution sequence, and returns it as a
* new Deferred. The branched Deferred's starting result will be shared with the
* parent at the point of the branch, even if further callbacks are added to the
* parent.
*
* All branches at the same stage in the execution sequence will receive the
* same starting value.
*
* @param {boolean=} opt_propagateCancel If cancel() is called on every child
* branch created with opt_propagateCancel, the parent will be canceled as
* well.
* @return {!goog.async.Deferred} A Deferred that will be started with the
* computed result from this stage in the execution sequence.
*/
goog.async.Deferred.prototype.branch = function(opt_propagateCancel) {
var d = new goog.async.Deferred();
this.chainDeferred(d);
if (opt_propagateCancel) {
d.parent_ = this;
this.branches_++;
}
return d;
};
/**
* @return {boolean} Whether the execution sequence has been started on this
* Deferred by invoking {@code callback} or {@code errback}.
*/
goog.async.Deferred.prototype.hasFired = function() {
return this.fired_;
};
/**
* @param {*} res The latest result in the execution sequence.
* @return {boolean} Whether the current result is an error that should cause
* the next errback to fire. May be overridden by subclasses to handle
* special error types.
* @protected
*/
goog.async.Deferred.prototype.isError = function(res) {
return res instanceof Error;
};
/**
* @return {boolean} Whether an errback exists in the remaining sequence.
* @private
*/
goog.async.Deferred.prototype.hasErrback_ = function() {
return goog.array.some(this.sequence_, function(sequenceRow) {
// The errback is the second element in the array.
return goog.isFunction(sequenceRow[1]);
});
};
/**
* Exhausts the execution sequence while a result is available. The result may
* be modified by callbacks or errbacks, and execution will block if the
* returned result is an incomplete Deferred.
*
* @private
*/
goog.async.Deferred.prototype.fire_ = function() {
if (this.unhandledExceptionTimeoutId_ && this.hasFired() &&
this.hasErrback_()) {
// It is possible to add errbacks after the Deferred has fired. If a new
// errback is added immediately after the Deferred encountered an unhandled
// error, but before that error is rethrown, cancel the rethrow.
goog.global.clearTimeout(this.unhandledExceptionTimeoutId_);
delete this.unhandledExceptionTimeoutId_;
}
if (this.parent_) {
this.parent_.branches_--;
delete this.parent_;
}
var res = this.result_;
var unhandledException = false;
var isNewlyBlocked = false;
while (this.sequence_.length && !this.blocked_) {
var sequenceEntry = this.sequence_.shift();
var callback = sequenceEntry[0];
var errback = sequenceEntry[1];
var scope = sequenceEntry[2];
var f = this.hadError_ ? errback : callback;
if (f) {
/** @preserveTry */
try {
var ret = f.call(scope || this.defaultScope_, res);
// If no result, then use previous result.
if (goog.isDef(ret)) {
// Bubble up the error as long as the return value hasn't changed.
this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));
this.result_ = res = ret;
}
if (res instanceof goog.async.Deferred) {
isNewlyBlocked = true;
this.blocked_ = true;
}
} catch (ex) {
res = ex;
this.hadError_ = true;
this.makeStackTraceLong_(res);
if (!this.hasErrback_()) {
// If an error is thrown with no additional errbacks in the queue,
// prepare to rethrow the error.
unhandledException = true;
}
}
}
}
this.result_ = res;
if (isNewlyBlocked) {
res.addCallbacks(
goog.bind(this.continue_, this, true /* isSuccess */),
goog.bind(this.continue_, this, false /* isSuccess */));
res.blocking_ = true;
} else if (goog.async.Deferred.STRICT_ERRORS && this.isError(res) &&
!(res instanceof goog.async.Deferred.CanceledError)) {
this.hadError_ = true;
unhandledException = true;
}
if (unhandledException) {
// Rethrow the unhandled error after a timeout. Execution will continue, but
// the error will be seen by global handlers and the user. The throw will
// be canceled if another errback is appended before the timeout executes.
// The error's original stack trace is preserved where available.
this.unhandledExceptionTimeoutId_ = goog.global.setTimeout(
goog.functions.fail(res), 0);
}
};
/**
* Creates a Deferred that has an initial result.
*
* @param {*=} opt_result The result.
* @return {!goog.async.Deferred} The new Deferred.
*/
goog.async.Deferred.succeed = function(opt_result) {
var d = new goog.async.Deferred();
d.callback(opt_result);
return d;
};
/**
* Creates a Deferred that has an initial error result.
*
* @param {*} res The error result.
* @return {!goog.async.Deferred} The new Deferred.
*/
goog.async.Deferred.fail = function(res) {
var d = new goog.async.Deferred();
d.errback(res);
return d;
};
/**
* Creates a Deferred that has already been canceled.
*
* @return {!goog.async.Deferred} The new Deferred.
*/
goog.async.Deferred.canceled = function() {
var d = new goog.async.Deferred();
d.cancel();
return d;
};
/**
* Normalizes values that may or may not be Deferreds.
*
* If the input value is a Deferred, the Deferred is branched (so the original
* execution sequence is not modified) and the input callback added to the new
* branch. The branch is returned to the caller.
*
* If the input value is not a Deferred, the callback will be executed
* immediately and an already firing Deferred will be returned to the caller.
*
* In the following (contrived) example, if <code>isImmediate</code> is true
* then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.
*
* <pre>
* var value;
* if (isImmediate) {
* value = 3;
* } else {
* value = new goog.async.Deferred();
* setTimeout(function() { value.callback(6); }, 2000);
* }
*
* var d = goog.async.Deferred.when(value, alert);
* </pre>
*
* @param {*} value Deferred or normal value to pass to the callback.
* @param {!function(this:T, ?):?} callback The callback to execute.
* @param {T=} opt_scope An optional scope to call the callback in.
* @return {!goog.async.Deferred} A new Deferred that will call the input
* callback with the input value.
* @template T
*/
goog.async.Deferred.when = function(value, callback, opt_scope) {
if (value instanceof goog.async.Deferred) {
return value.branch(true).addCallback(callback, opt_scope);
} else {
return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);
}
};
/**
* An error sub class that is used when a Deferred has already been called.
* @param {!goog.async.Deferred} deferred The Deferred.
*
* @constructor
* @extends {goog.debug.Error}
*/
goog.async.Deferred.AlreadyCalledError = function(deferred) {
goog.debug.Error.call(this);
/**
* The Deferred that raised this error.
* @type {goog.async.Deferred}
*/
this.deferred = deferred;
};
goog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);
/** @override */
goog.async.Deferred.AlreadyCalledError.prototype.message =
'Deferred has already fired';
/** @override */
goog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';
/**
* An error sub class that is used when a Deferred is canceled.
*
* @param {!goog.async.Deferred} deferred The Deferred object.
* @constructor
* @extends {goog.debug.Error}
*/
goog.async.Deferred.CanceledError = function(deferred) {
goog.debug.Error.call(this);
/**
* The Deferred that raised this error.
* @type {goog.async.Deferred}
*/
this.deferred = deferred;
};
goog.inherits(goog.async.Deferred.CanceledError, goog.debug.Error);
/** @override */
goog.async.Deferred.CanceledError.prototype.message = 'Deferred was canceled';
/** @override */
goog.async.Deferred.CanceledError.prototype.name = 'CanceledError';
@@ -0,0 +1,205 @@
// 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.base(this, 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.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;
});
};