Path: blob/trunk/third_party/closure/goog/mochikit/async/deferred.js
4571 views
// Copyright 2007 Bob Ippolito. All Rights Reserved.1// Modifications Copyright 2009 The Closure Library Authors. All Rights2// Reserved.34/**5* @license Portions of this code are from MochiKit, received by6* The Closure Authors under the MIT license. All other code is Copyright7* 2005-2009 The Closure Authors. All Rights Reserved.8*/910/**11* @fileoverview Classes for tracking asynchronous operations and handling the12* results. The Deferred object here is patterned after the Deferred object in13* the Twisted python networking framework.14*15* See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html16*17* Based on the Dojo code which in turn is based on the MochiKit code.18*19* @author [email protected] (Erik Arvidsson)20* @author [email protected] (Shawn Brenneman)21*/2223goog.provide('goog.async.Deferred');24goog.provide('goog.async.Deferred.AlreadyCalledError');25goog.provide('goog.async.Deferred.CanceledError');2627goog.require('goog.Promise');28goog.require('goog.Thenable');29goog.require('goog.array');30goog.require('goog.asserts');31goog.require('goog.debug.Error');32333435/**36* A Deferred represents the result of an asynchronous operation. A Deferred37* instance has no result when it is created, and is "fired" (given an initial38* result) by calling `callback` or `errback`.39*40* Once fired, the result is passed through a sequence of callback functions41* registered with `addCallback` or `addErrback`. The functions may42* mutate the result before it is passed to the next function in the sequence.43*44* Callbacks and errbacks may be added at any time, including after the Deferred45* has been "fired". If there are no pending actions in the execution sequence46* of a fired Deferred, any new callback functions will be called with the last47* computed result. Adding a callback function is the only way to access the48* result of the Deferred.49*50* If a Deferred operation is canceled, an optional user-provided cancellation51* function is invoked which may perform any special cleanup, followed by firing52* the Deferred's errback sequence with a `CanceledError`. If the53* Deferred has already fired, cancellation is ignored.54*55* Deferreds may be templated to a specific type they produce using generics56* with syntax such as:57*58* /** @type {goog.async.Deferred<string>} *\59* var d = new goog.async.Deferred();60* // Compiler can infer that foo is a string.61* d.addCallback(function(foo) {...});62* d.callback('string'); // Checked to be passed a string63*64* Since deferreds are often used to produce different values across a chain,65* the type information is not propagated across chains, but rather only66* associated with specifically cast objects.67*68* @param {Function=} opt_onCancelFunction A function that will be called if the69* Deferred is canceled. If provided, this function runs before the70* Deferred is fired with a `CanceledError`.71* @param {Object=} opt_defaultScope The default object context to call72* callbacks and errbacks in.73* @constructor74* @implements {goog.Thenable<VALUE>}75* @template VALUE76*/77goog.async.Deferred = function(opt_onCancelFunction, opt_defaultScope) {78/**79* Entries in the sequence are arrays containing a callback, an errback, and80* an optional scope. The callback or errback in an entry may be null.81* @type {!Array<!Array>}82* @private83*/84this.sequence_ = [];8586/**87* Optional function that will be called if the Deferred is canceled.88* @type {Function|undefined}89* @private90*/91this.onCancelFunction_ = opt_onCancelFunction;9293/**94* The default scope to execute callbacks and errbacks in.95* @type {Object}96* @private97*/98this.defaultScope_ = opt_defaultScope || null;99100/**101* Whether the Deferred has been fired.102* @type {boolean}103* @private104*/105this.fired_ = false;106107/**108* Whether the last result in the execution sequence was an error.109* @type {boolean}110* @private111*/112this.hadError_ = false;113114/**115* The current Deferred result, updated as callbacks and errbacks are116* executed.117* @type {*}118* @private119*/120this.result_ = undefined;121122/**123* Whether the Deferred is blocked waiting on another Deferred to fire. If a124* callback or errback returns a Deferred as a result, the execution sequence125* is blocked until that Deferred result becomes available.126* @type {boolean}127* @private128*/129this.blocked_ = false;130131/**132* Whether this Deferred is blocking execution of another Deferred. If this133* instance was returned as a result in another Deferred's execution134* sequence,that other Deferred becomes blocked until this instance's135* execution sequence completes. No additional callbacks may be added to a136* Deferred once it is blocking another instance.137* @type {boolean}138* @private139*/140this.blocking_ = false;141142/**143* Whether the Deferred has been canceled without having a custom cancel144* function.145* @type {boolean}146* @private147*/148this.silentlyCanceled_ = false;149150/**151* If an error is thrown during Deferred execution with no errback to catch152* it, the error is rethrown after a timeout. Reporting the error after a153* timeout allows execution to continue in the calling context (empty when154* no error is scheduled).155* @type {number}156* @private157*/158this.unhandledErrorId_ = 0;159160/**161* If this Deferred was created by branch(), this will be the "parent"162* Deferred.163* @type {?goog.async.Deferred}164* @private165*/166this.parent_ = null;167168/**169* The number of Deferred objects that have been branched off this one. This170* will be decremented whenever a branch is fired or canceled.171* @type {number}172* @private173*/174this.branches_ = 0;175176if (goog.async.Deferred.LONG_STACK_TRACES) {177/**178* Holds the stack trace at time of deferred creation if the JS engine179* provides the Error.captureStackTrace API.180* @private {?string}181*/182this.constructorStack_ = null;183if (Error.captureStackTrace) {184var target = { stack: '' };185Error.captureStackTrace(target, goog.async.Deferred);186// Check if Error.captureStackTrace worked. It fails in gjstest.187if (typeof target.stack == 'string') {188// Remove first line and force stringify to prevent memory leak due to189// holding on to actual stack frames.190this.constructorStack_ = target.stack.replace(/^[^\n]*\n/, '');191}192}193}194};195196197/**198* @define {boolean} Whether unhandled errors should always get rethrown to the199* global scope. Defaults to false.200*/201goog.async.Deferred.STRICT_ERRORS =202goog.define('goog.async.Deferred.STRICT_ERRORS', false);203204205/**206* @define {boolean} Whether to attempt to make stack traces long. Defaults to207* false.208*/209goog.async.Deferred.LONG_STACK_TRACES =210goog.define('goog.async.Deferred.LONG_STACK_TRACES', false);211212213/**214* Cancels a Deferred that has not yet been fired, or is blocked on another215* deferred operation. If this Deferred is waiting for a blocking Deferred to216* fire, the blocking Deferred will also be canceled.217*218* If this Deferred was created by calling branch() on a parent Deferred with219* opt_propagateCancel set to true, the parent may also be canceled. If220* opt_deepCancel is set, cancel() will be called on the parent (as well as any221* other ancestors if the parent is also a branch). If one or more branches were222* created with opt_propagateCancel set to true, the parent will be canceled if223* cancel() is called on all of those branches.224*225* @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even226* if cancel() hasn't been called on some of the parent's branches. Has no227* effect on a branch without opt_propagateCancel set to true.228*/229goog.async.Deferred.prototype.cancel = function(opt_deepCancel) {230if (!this.hasFired()) {231if (this.parent_) {232// Get rid of the parent reference before potentially running the parent's233// canceler function to ensure that this cancellation isn't234// double-counted.235var parent = this.parent_;236delete this.parent_;237if (opt_deepCancel) {238parent.cancel(opt_deepCancel);239} else {240parent.branchCancel_();241}242}243244if (this.onCancelFunction_) {245// Call in user-specified scope.246this.onCancelFunction_.call(this.defaultScope_, this);247} else {248this.silentlyCanceled_ = true;249}250if (!this.hasFired()) {251this.errback(new goog.async.Deferred.CanceledError(this));252}253} else if (this.result_ instanceof goog.async.Deferred) {254this.result_.cancel();255}256};257258259/**260* Handle a single branch being canceled. Once all branches are canceled, this261* Deferred will be canceled as well.262*263* @private264*/265goog.async.Deferred.prototype.branchCancel_ = function() {266this.branches_--;267if (this.branches_ <= 0) {268this.cancel();269}270};271272273/**274* Called after a blocking Deferred fires. Unblocks this Deferred and resumes275* its execution sequence.276*277* @param {boolean} isSuccess Whether the result is a success or an error.278* @param {*} res The result of the blocking Deferred.279* @private280*/281goog.async.Deferred.prototype.continue_ = function(isSuccess, res) {282this.blocked_ = false;283this.updateResult_(isSuccess, res);284};285286287/**288* Updates the current result based on the success or failure of the last action289* in the execution sequence.290*291* @param {boolean} isSuccess Whether the new result is a success or an error.292* @param {*} res The result.293* @private294*/295goog.async.Deferred.prototype.updateResult_ = function(isSuccess, res) {296this.fired_ = true;297this.result_ = res;298this.hadError_ = !isSuccess;299this.fire_();300};301302303/**304* Verifies that the Deferred has not yet been fired.305*306* @private307* @throws {Error} If this has already been fired.308*/309goog.async.Deferred.prototype.check_ = function() {310if (this.hasFired()) {311if (!this.silentlyCanceled_) {312throw new goog.async.Deferred.AlreadyCalledError(this);313}314this.silentlyCanceled_ = false;315}316};317318319/**320* Fire the execution sequence for this Deferred by passing the starting result321* to the first registered callback.322* @param {VALUE=} opt_result The starting result.323*/324goog.async.Deferred.prototype.callback = function(opt_result) {325this.check_();326this.assertNotDeferred_(opt_result);327this.updateResult_(true /* isSuccess */, opt_result);328};329330331/**332* Fire the execution sequence for this Deferred by passing the starting error333* result to the first registered errback.334* @param {*=} opt_result The starting error.335*/336goog.async.Deferred.prototype.errback = function(opt_result) {337this.check_();338this.assertNotDeferred_(opt_result);339this.makeStackTraceLong_(opt_result);340this.updateResult_(false /* isSuccess */, opt_result);341};342343344/**345* Attempt to make the error's stack trace be long in that it contains the346* stack trace from the point where the deferred was created on top of the347* current stack trace to give additional context.348* @param {*} error349* @private350*/351goog.async.Deferred.prototype.makeStackTraceLong_ = function(error) {352if (!goog.async.Deferred.LONG_STACK_TRACES) {353return;354}355if (this.constructorStack_ && goog.isObject(error) && error.stack &&356// Stack looks like it was system generated. See357// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi358(/^[^\n]+(\n [^\n]+)+/).test(error.stack)) {359error.stack = error.stack + '\nDEFERRED OPERATION:\n' +360this.constructorStack_;361}362};363364365/**366* Asserts that an object is not a Deferred.367* @param {*} obj The object to test.368* @throws {Error} Throws an exception if the object is a Deferred.369* @private370*/371goog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {372goog.asserts.assert(373!(obj instanceof goog.async.Deferred),374'An execution sequence may not be initiated with a blocking Deferred.');375};376377378/**379* Register a callback function to be called with a successful result. If no380* value is returned by the callback function, the result value is unchanged. If381* a new value is returned, it becomes the Deferred result and will be passed to382* the next callback in the execution sequence.383*384* If the function throws an error, the error becomes the new result and will be385* passed to the next errback in the execution chain.386*387* If the function returns a Deferred, the execution sequence will be blocked388* until that Deferred fires. Its result will be passed to the next callback (or389* errback if it is an error result) in this Deferred's execution sequence.390*391* @param {function(this:T,VALUE):?} cb The function to be called with a392* successful result.393* @param {T=} opt_scope An optional scope to call the callback in.394* @return {!goog.async.Deferred} This Deferred.395* @template T396*/397goog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {398return this.addCallbacks(cb, null, opt_scope);399};400401402/**403* Register a callback function to be called with an error result. If no value404* is returned by the function, the error result is unchanged. If a new error405* value is returned or thrown, that error becomes the Deferred result and will406* be passed to the next errback in the execution sequence.407*408* If the errback function handles the error by returning a non-error value,409* that result will be passed to the next normal callback in the sequence.410*411* If the function returns a Deferred, the execution sequence will be blocked412* until that Deferred fires. Its result will be passed to the next callback (or413* errback if it is an error result) in this Deferred's execution sequence.414*415* @param {function(this:T,?):?} eb The function to be called on an416* unsuccessful result.417* @param {T=} opt_scope An optional scope to call the errback in.418* @return {!goog.async.Deferred<VALUE>} This Deferred.419* @template T420*/421goog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {422return this.addCallbacks(null, eb, opt_scope);423};424425426/**427* Registers one function as both a callback and errback.428*429* @param {function(this:T,?):?} f The function to be called on any result.430* @param {T=} opt_scope An optional scope to call the function in.431* @return {!goog.async.Deferred} This Deferred.432* @template T433*/434goog.async.Deferred.prototype.addBoth = function(f, opt_scope) {435return this.addCallbacks(f, f, opt_scope);436};437438439/**440* Like addBoth, but propagates uncaught exceptions in the errback.441*442* @param {function(this:T,?):?} f The function to be called on any result.443* @param {T=} opt_scope An optional scope to call the function in.444* @return {!goog.async.Deferred<VALUE>} This Deferred.445* @template T446*/447goog.async.Deferred.prototype.addFinally = function(f, opt_scope) {448return this.addCallbacks(f, function(err) {449var result = f.call(/** @type {?} */ (this), err);450if (result === undefined) {451throw err;452}453return result;454}, opt_scope);455};456457458/**459* Registers a callback function and an errback function at the same position460* in the execution sequence. Only one of these functions will execute,461* depending on the error state during the execution sequence.462*463* NOTE: This is not equivalent to {@code def.addCallback().addErrback()}! If464* the callback is invoked, the errback will be skipped, and vice versa.465*466* @param {?(function(this:T,VALUE):?)} cb The function to be called on a467* successful result.468* @param {?(function(this:T,?):?)} eb The function to be called on an469* unsuccessful result.470* @param {T=} opt_scope An optional scope to call the functions in.471* @return {!goog.async.Deferred} This Deferred.472* @template T473*/474goog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {475goog.asserts.assert(!this.blocking_, 'Blocking Deferreds can not be re-used');476this.sequence_.push([cb, eb, opt_scope]);477if (this.hasFired()) {478this.fire_();479}480return this;481};482483484/**485* Implements {@see goog.Thenable} for seamless integration with486* {@see goog.Promise}.487* Deferred results are mutable and may represent multiple values over488* their lifetime. Calling `then` on a Deferred returns a Promise489* with the result of the Deferred at that point in its callback chain.490* Note that if the Deferred result is never mutated, and only491* `then` calls are made, the Deferred will behave like a Promise.492*493* @override494*/495goog.async.Deferred.prototype.then = function(opt_onFulfilled, opt_onRejected,496opt_context) {497var resolve, reject;498var promise = new goog.Promise(function(res, rej) {499// Copying resolvers to outer scope, so that they are available when the500// deferred callback fires (which may be synchronous).501resolve = res;502reject = rej;503});504this.addCallbacks(resolve, function(reason) {505if (reason instanceof goog.async.Deferred.CanceledError) {506promise.cancel();507} else {508reject(reason);509}510});511return promise.then(opt_onFulfilled, opt_onRejected, opt_context);512};513goog.Thenable.addImplementation(goog.async.Deferred);514515516/**517* Links another Deferred to the end of this Deferred's execution sequence. The518* result of this execution sequence will be passed as the starting result for519* the chained Deferred, invoking either its first callback or errback.520*521* @param {!goog.async.Deferred} otherDeferred The Deferred to chain.522* @return {!goog.async.Deferred} This Deferred.523*/524goog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {525this.addCallbacks(526otherDeferred.callback, otherDeferred.errback, otherDeferred);527return this;528};529530531/**532* Makes this Deferred wait for another Deferred's execution sequence to533* complete before continuing.534*535* This is equivalent to adding a callback that returns `otherDeferred`,536* but doesn't prevent additional callbacks from being added to537* `otherDeferred`.538*539* @param {!goog.async.Deferred|!goog.Thenable} otherDeferred The Deferred540* to wait for.541* @return {!goog.async.Deferred} This Deferred.542*/543goog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {544if (!(otherDeferred instanceof goog.async.Deferred)) {545// The Thenable case.546return this.addCallback(function() {547return otherDeferred;548});549}550return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));551};552553554/**555* Creates a branch off this Deferred's execution sequence, and returns it as a556* new Deferred. The branched Deferred's starting result will be shared with the557* parent at the point of the branch, even if further callbacks are added to the558* parent.559*560* All branches at the same stage in the execution sequence will receive the561* same starting value.562*563* @param {boolean=} opt_propagateCancel If cancel() is called on every child564* branch created with opt_propagateCancel, the parent will be canceled as565* well.566* @return {!goog.async.Deferred<VALUE>} A Deferred that will be started with567* the computed result from this stage in the execution sequence.568*/569goog.async.Deferred.prototype.branch = function(opt_propagateCancel) {570var d = new goog.async.Deferred();571this.chainDeferred(d);572if (opt_propagateCancel) {573d.parent_ = this;574this.branches_++;575}576return d;577};578579580/**581* @return {boolean} Whether the execution sequence has been started on this582* Deferred by invoking `callback` or `errback`.583*/584goog.async.Deferred.prototype.hasFired = function() {585return this.fired_;586};587588589/**590* @param {*} res The latest result in the execution sequence.591* @return {boolean} Whether the current result is an error that should cause592* the next errback to fire. May be overridden by subclasses to handle593* special error types.594* @protected595*/596goog.async.Deferred.prototype.isError = function(res) {597return res instanceof Error;598};599600601/**602* @return {boolean} Whether an errback exists in the remaining sequence.603* @private604*/605goog.async.Deferred.prototype.hasErrback_ = function() {606return goog.array.some(this.sequence_, function(sequenceRow) {607// The errback is the second element in the array.608return goog.isFunction(sequenceRow[1]);609});610};611612613/**614* Exhausts the execution sequence while a result is available. The result may615* be modified by callbacks or errbacks, and execution will block if the616* returned result is an incomplete Deferred.617*618* @private619*/620goog.async.Deferred.prototype.fire_ = function() {621if (this.unhandledErrorId_ && this.hasFired() && this.hasErrback_()) {622// It is possible to add errbacks after the Deferred has fired. If a new623// errback is added immediately after the Deferred encountered an unhandled624// error, but before that error is rethrown, the error is unscheduled.625goog.async.Deferred.unscheduleError_(this.unhandledErrorId_);626this.unhandledErrorId_ = 0;627}628629if (this.parent_) {630this.parent_.branches_--;631delete this.parent_;632}633634var res = this.result_;635var unhandledException = false;636var isNewlyBlocked = false;637638while (this.sequence_.length && !this.blocked_) {639var sequenceEntry = this.sequence_.shift();640641var callback = sequenceEntry[0];642var errback = sequenceEntry[1];643var scope = sequenceEntry[2];644645var f = this.hadError_ ? errback : callback;646if (f) {647648try {649var ret = f.call(scope || this.defaultScope_, res);650651// If no result, then use previous result.652if (ret !== undefined) {653// Bubble up the error as long as the return value hasn't changed.654this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));655this.result_ = res = ret;656}657658if (goog.Thenable.isImplementedBy(res) ||659(typeof goog.global['Promise'] === 'function' &&660res instanceof goog.global['Promise'])) {661isNewlyBlocked = true;662this.blocked_ = true;663}664665} catch (ex) {666res = ex;667this.hadError_ = true;668this.makeStackTraceLong_(res);669670if (!this.hasErrback_()) {671// If an error is thrown with no additional errbacks in the queue,672// prepare to rethrow the error.673unhandledException = true;674}675}676}677}678679this.result_ = res;680681if (isNewlyBlocked) {682var onCallback = goog.bind(this.continue_, this, true /* isSuccess */);683var onErrback = goog.bind(this.continue_, this, false /* isSuccess */);684685if (res instanceof goog.async.Deferred) {686res.addCallbacks(onCallback, onErrback);687res.blocking_ = true;688} else {689/** @type {!IThenable} */ (res).then(onCallback, onErrback);690}691} else if (goog.async.Deferred.STRICT_ERRORS && this.isError(res) &&692!(res instanceof goog.async.Deferred.CanceledError)) {693this.hadError_ = true;694unhandledException = true;695}696697if (unhandledException) {698// Rethrow the unhandled error after a timeout. Execution will continue, but699// the error will be seen by global handlers and the user. The throw will700// be canceled if another errback is appended before the timeout executes.701// The error's original stack trace is preserved where available.702this.unhandledErrorId_ = goog.async.Deferred.scheduleError_(res);703}704};705706707/**708* Creates a Deferred that has an initial result.709*710* @param {*=} opt_result The result.711* @return {!goog.async.Deferred} The new Deferred.712*/713goog.async.Deferred.succeed = function(opt_result) {714var d = new goog.async.Deferred();715d.callback(opt_result);716return d;717};718719720/**721* Creates a Deferred that fires when the given promise resolves.722* Use only during migration to Promises.723*724* Note: If the promise resolves to a thenable value (which is not allowed by725* conforming promise implementations), then the deferred may behave726* unexpectedly as it tries to wait on it. This should not be a risk when using727* goog.Promise, goog.async.Deferred, or native Promise objects.728*729* @param {!IThenable<T>} promise730* @return {!goog.async.Deferred<T>} The new Deferred.731* @template T732*/733goog.async.Deferred.fromPromise = function(promise) {734var d = new goog.async.Deferred();735promise.then(736function(value) {737d.callback(value);738},739function(error) {740d.errback(error);741});742return d;743};744745746/**747* Creates a Deferred that has an initial error result.748*749* @param {*} res The error result.750* @return {!goog.async.Deferred} The new Deferred.751*/752goog.async.Deferred.fail = function(res) {753var d = new goog.async.Deferred();754d.errback(res);755return d;756};757758759/**760* Creates a Deferred that has already been canceled.761*762* @return {!goog.async.Deferred} The new Deferred.763*/764goog.async.Deferred.canceled = function() {765var d = new goog.async.Deferred();766d.cancel();767return d;768};769770771/**772* Normalizes values that may or may not be Deferreds.773*774* If the input value is a Deferred, the Deferred is branched (so the original775* execution sequence is not modified) and the input callback added to the new776* branch. The branch is returned to the caller.777*778* If the input value is not a Deferred, the callback will be executed779* immediately and an already firing Deferred will be returned to the caller.780*781* In the following (contrived) example, if <code>isImmediate</code> is true782* then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.783*784* <pre>785* var value;786* if (isImmediate) {787* value = 3;788* } else {789* value = new goog.async.Deferred();790* setTimeout(function() { value.callback(6); }, 2000);791* }792*793* var d = goog.async.Deferred.when(value, alert);794* </pre>795*796* @param {*} value Deferred or normal value to pass to the callback.797* @param {function(this:T, ?):?} callback The callback to execute.798* @param {T=} opt_scope An optional scope to call the callback in.799* @return {!goog.async.Deferred} A new Deferred that will call the input800* callback with the input value.801* @template T802*/803goog.async.Deferred.when = function(value, callback, opt_scope) {804if (value instanceof goog.async.Deferred) {805return value.branch(true).addCallback(callback, opt_scope);806} else {807return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);808}809};810811812813/**814* An error sub class that is used when a Deferred has already been called.815* @param {!goog.async.Deferred} deferred The Deferred.816*817* @constructor818* @extends {goog.debug.Error}819*/820goog.async.Deferred.AlreadyCalledError = function(deferred) {821goog.debug.Error.call(this);822823/**824* The Deferred that raised this error.825* @type {goog.async.Deferred}826*/827this.deferred = deferred;828};829goog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);830831832/** @override */833goog.async.Deferred.AlreadyCalledError.prototype.message =834'Deferred has already fired';835836837/** @override */838goog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';839840841842/**843* An error sub class that is used when a Deferred is canceled.844*845* @param {!goog.async.Deferred} deferred The Deferred object.846* @constructor847* @extends {goog.debug.Error}848*/849goog.async.Deferred.CanceledError = function(deferred) {850goog.debug.Error.call(this);851852/**853* The Deferred that raised this error.854* @type {goog.async.Deferred}855*/856this.deferred = deferred;857};858goog.inherits(goog.async.Deferred.CanceledError, goog.debug.Error);859860861/** @override */862goog.async.Deferred.CanceledError.prototype.message = 'Deferred was canceled';863864865/** @override */866goog.async.Deferred.CanceledError.prototype.name = 'CanceledError';867868869870/**871* Wrapper around errors that are scheduled to be thrown by failing deferreds872* after a timeout.873*874* @param {*} error Error from a failing deferred.875* @constructor876* @final877* @private878* @struct879*/880goog.async.Deferred.Error_ = function(error) {881/** @const @private {number} */882this.id_ = goog.global.setTimeout(goog.bind(this.throwError, this), 0);883884/** @const @private {*} */885this.error_ = error;886};887888889/**890* Actually throws the error and removes it from the list of pending891* deferred errors.892*/893goog.async.Deferred.Error_.prototype.throwError = function() {894goog.asserts.assert(goog.async.Deferred.errorMap_[this.id_],895'Cannot throw an error that is not scheduled.');896delete goog.async.Deferred.errorMap_[this.id_];897throw this.error_;898};899900901/**902* Resets the error throw timer.903*/904goog.async.Deferred.Error_.prototype.resetTimer = function() {905goog.global.clearTimeout(this.id_);906};907908909/**910* Map of unhandled errors scheduled to be rethrown in a future timestep.911* @private {!Object<(number|string), goog.async.Deferred.Error_>}912*/913goog.async.Deferred.errorMap_ = {};914915916/**917* Schedules an error to be thrown after a delay.918* @param {*} error Error from a failing deferred.919* @return {number} Id of the error.920* @private921*/922goog.async.Deferred.scheduleError_ = function(error) {923var deferredError = new goog.async.Deferred.Error_(error);924goog.async.Deferred.errorMap_[deferredError.id_] = deferredError;925return deferredError.id_;926};927928929/**930* Unschedules an error from being thrown.931* @param {number} id Id of the deferred error to unschedule.932* @private933*/934goog.async.Deferred.unscheduleError_ = function(id) {935var error = goog.async.Deferred.errorMap_[id];936if (error) {937error.resetTimer();938delete goog.async.Deferred.errorMap_[id];939}940};941942943/**944* Asserts that there are no pending deferred errors. If there are any945* scheduled errors, one will be thrown immediately to make this function fail.946*/947goog.async.Deferred.assertNoErrors = function() {948var map = goog.async.Deferred.errorMap_;949for (var key in map) {950var error = map[key];951error.resetTimer();952error.throwError();953}954};955956957