deferred.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. // Copyright 2007 Bob Ippolito. All Rights Reserved.
  2. // Modifications Copyright 2009 The Closure Library Authors. All Rights
  3. // Reserved.
  4. /**
  5. * @license Portions of this code are from MochiKit, received by
  6. * The Closure Authors under the MIT license. All other code is Copyright
  7. * 2005-2009 The Closure Authors. All Rights Reserved.
  8. */
  9. /**
  10. * @fileoverview Classes for tracking asynchronous operations and handling the
  11. * results. The Deferred object here is patterned after the Deferred object in
  12. * the Twisted python networking framework.
  13. *
  14. * See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
  15. *
  16. * Based on the Dojo code which in turn is based on the MochiKit code.
  17. *
  18. * @author arv@google.com (Erik Arvidsson)
  19. * @author brenneman@google.com (Shawn Brenneman)
  20. */
  21. goog.provide('goog.async.Deferred');
  22. goog.provide('goog.async.Deferred.AlreadyCalledError');
  23. goog.provide('goog.async.Deferred.CanceledError');
  24. goog.require('goog.Promise');
  25. goog.require('goog.Thenable');
  26. goog.require('goog.array');
  27. goog.require('goog.asserts');
  28. goog.require('goog.debug.Error');
  29. /**
  30. * A Deferred represents the result of an asynchronous operation. A Deferred
  31. * instance has no result when it is created, and is "fired" (given an initial
  32. * result) by calling {@code callback} or {@code errback}.
  33. *
  34. * Once fired, the result is passed through a sequence of callback functions
  35. * registered with {@code addCallback} or {@code addErrback}. The functions may
  36. * mutate the result before it is passed to the next function in the sequence.
  37. *
  38. * Callbacks and errbacks may be added at any time, including after the Deferred
  39. * has been "fired". If there are no pending actions in the execution sequence
  40. * of a fired Deferred, any new callback functions will be called with the last
  41. * computed result. Adding a callback function is the only way to access the
  42. * result of the Deferred.
  43. *
  44. * If a Deferred operation is canceled, an optional user-provided cancellation
  45. * function is invoked which may perform any special cleanup, followed by firing
  46. * the Deferred's errback sequence with a {@code CanceledError}. If the
  47. * Deferred has already fired, cancellation is ignored.
  48. *
  49. * Deferreds may be templated to a specific type they produce using generics
  50. * with syntax such as:
  51. *
  52. * /** @type {goog.async.Deferred<string>} *\
  53. * var d = new goog.async.Deferred();
  54. * // Compiler can infer that foo is a string.
  55. * d.addCallback(function(foo) {...});
  56. * d.callback('string'); // Checked to be passed a string
  57. *
  58. * Since deferreds are often used to produce different values across a chain,
  59. * the type information is not propagated across chains, but rather only
  60. * associated with specifically cast objects.
  61. *
  62. * @param {Function=} opt_onCancelFunction A function that will be called if the
  63. * Deferred is canceled. If provided, this function runs before the
  64. * Deferred is fired with a {@code CanceledError}.
  65. * @param {Object=} opt_defaultScope The default object context to call
  66. * callbacks and errbacks in.
  67. * @constructor
  68. * @implements {goog.Thenable<VALUE>}
  69. * @template VALUE
  70. */
  71. goog.async.Deferred = function(opt_onCancelFunction, opt_defaultScope) {
  72. /**
  73. * Entries in the sequence are arrays containing a callback, an errback, and
  74. * an optional scope. The callback or errback in an entry may be null.
  75. * @type {!Array<!Array>}
  76. * @private
  77. */
  78. this.sequence_ = [];
  79. /**
  80. * Optional function that will be called if the Deferred is canceled.
  81. * @type {Function|undefined}
  82. * @private
  83. */
  84. this.onCancelFunction_ = opt_onCancelFunction;
  85. /**
  86. * The default scope to execute callbacks and errbacks in.
  87. * @type {Object}
  88. * @private
  89. */
  90. this.defaultScope_ = opt_defaultScope || null;
  91. /**
  92. * Whether the Deferred has been fired.
  93. * @type {boolean}
  94. * @private
  95. */
  96. this.fired_ = false;
  97. /**
  98. * Whether the last result in the execution sequence was an error.
  99. * @type {boolean}
  100. * @private
  101. */
  102. this.hadError_ = false;
  103. /**
  104. * The current Deferred result, updated as callbacks and errbacks are
  105. * executed.
  106. * @type {*}
  107. * @private
  108. */
  109. this.result_ = undefined;
  110. /**
  111. * Whether the Deferred is blocked waiting on another Deferred to fire. If a
  112. * callback or errback returns a Deferred as a result, the execution sequence
  113. * is blocked until that Deferred result becomes available.
  114. * @type {boolean}
  115. * @private
  116. */
  117. this.blocked_ = false;
  118. /**
  119. * Whether this Deferred is blocking execution of another Deferred. If this
  120. * instance was returned as a result in another Deferred's execution
  121. * sequence,that other Deferred becomes blocked until this instance's
  122. * execution sequence completes. No additional callbacks may be added to a
  123. * Deferred once it is blocking another instance.
  124. * @type {boolean}
  125. * @private
  126. */
  127. this.blocking_ = false;
  128. /**
  129. * Whether the Deferred has been canceled without having a custom cancel
  130. * function.
  131. * @type {boolean}
  132. * @private
  133. */
  134. this.silentlyCanceled_ = false;
  135. /**
  136. * If an error is thrown during Deferred execution with no errback to catch
  137. * it, the error is rethrown after a timeout. Reporting the error after a
  138. * timeout allows execution to continue in the calling context (empty when
  139. * no error is scheduled).
  140. * @type {number}
  141. * @private
  142. */
  143. this.unhandledErrorId_ = 0;
  144. /**
  145. * If this Deferred was created by branch(), this will be the "parent"
  146. * Deferred.
  147. * @type {goog.async.Deferred}
  148. * @private
  149. */
  150. this.parent_ = null;
  151. /**
  152. * The number of Deferred objects that have been branched off this one. This
  153. * will be decremented whenever a branch is fired or canceled.
  154. * @type {number}
  155. * @private
  156. */
  157. this.branches_ = 0;
  158. if (goog.async.Deferred.LONG_STACK_TRACES) {
  159. /**
  160. * Holds the stack trace at time of deferred creation if the JS engine
  161. * provides the Error.captureStackTrace API.
  162. * @private {?string}
  163. */
  164. this.constructorStack_ = null;
  165. if (Error.captureStackTrace) {
  166. var target = { stack: '' };
  167. Error.captureStackTrace(target, goog.async.Deferred);
  168. // Check if Error.captureStackTrace worked. It fails in gjstest.
  169. if (typeof target.stack == 'string') {
  170. // Remove first line and force stringify to prevent memory leak due to
  171. // holding on to actual stack frames.
  172. this.constructorStack_ = target.stack.replace(/^[^\n]*\n/, '');
  173. }
  174. }
  175. }
  176. };
  177. /**
  178. * @define {boolean} Whether unhandled errors should always get rethrown to the
  179. * global scope. Defaults to the value of goog.DEBUG.
  180. */
  181. goog.define('goog.async.Deferred.STRICT_ERRORS', false);
  182. /**
  183. * @define {boolean} Whether to attempt to make stack traces long. Defaults to
  184. * the value of goog.DEBUG.
  185. */
  186. goog.define('goog.async.Deferred.LONG_STACK_TRACES', false);
  187. /**
  188. * Cancels a Deferred that has not yet been fired, or is blocked on another
  189. * deferred operation. If this Deferred is waiting for a blocking Deferred to
  190. * fire, the blocking Deferred will also be canceled.
  191. *
  192. * If this Deferred was created by calling branch() on a parent Deferred with
  193. * opt_propagateCancel set to true, the parent may also be canceled. If
  194. * opt_deepCancel is set, cancel() will be called on the parent (as well as any
  195. * other ancestors if the parent is also a branch). If one or more branches were
  196. * created with opt_propagateCancel set to true, the parent will be canceled if
  197. * cancel() is called on all of those branches.
  198. *
  199. * @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even
  200. * if cancel() hasn't been called on some of the parent's branches. Has no
  201. * effect on a branch without opt_propagateCancel set to true.
  202. */
  203. goog.async.Deferred.prototype.cancel = function(opt_deepCancel) {
  204. if (!this.hasFired()) {
  205. if (this.parent_) {
  206. // Get rid of the parent reference before potentially running the parent's
  207. // canceler function to ensure that this cancellation isn't
  208. // double-counted.
  209. var parent = this.parent_;
  210. delete this.parent_;
  211. if (opt_deepCancel) {
  212. parent.cancel(opt_deepCancel);
  213. } else {
  214. parent.branchCancel_();
  215. }
  216. }
  217. if (this.onCancelFunction_) {
  218. // Call in user-specified scope.
  219. this.onCancelFunction_.call(this.defaultScope_, this);
  220. } else {
  221. this.silentlyCanceled_ = true;
  222. }
  223. if (!this.hasFired()) {
  224. this.errback(new goog.async.Deferred.CanceledError(this));
  225. }
  226. } else if (this.result_ instanceof goog.async.Deferred) {
  227. this.result_.cancel();
  228. }
  229. };
  230. /**
  231. * Handle a single branch being canceled. Once all branches are canceled, this
  232. * Deferred will be canceled as well.
  233. *
  234. * @private
  235. */
  236. goog.async.Deferred.prototype.branchCancel_ = function() {
  237. this.branches_--;
  238. if (this.branches_ <= 0) {
  239. this.cancel();
  240. }
  241. };
  242. /**
  243. * Called after a blocking Deferred fires. Unblocks this Deferred and resumes
  244. * its execution sequence.
  245. *
  246. * @param {boolean} isSuccess Whether the result is a success or an error.
  247. * @param {*} res The result of the blocking Deferred.
  248. * @private
  249. */
  250. goog.async.Deferred.prototype.continue_ = function(isSuccess, res) {
  251. this.blocked_ = false;
  252. this.updateResult_(isSuccess, res);
  253. };
  254. /**
  255. * Updates the current result based on the success or failure of the last action
  256. * in the execution sequence.
  257. *
  258. * @param {boolean} isSuccess Whether the new result is a success or an error.
  259. * @param {*} res The result.
  260. * @private
  261. */
  262. goog.async.Deferred.prototype.updateResult_ = function(isSuccess, res) {
  263. this.fired_ = true;
  264. this.result_ = res;
  265. this.hadError_ = !isSuccess;
  266. this.fire_();
  267. };
  268. /**
  269. * Verifies that the Deferred has not yet been fired.
  270. *
  271. * @private
  272. * @throws {Error} If this has already been fired.
  273. */
  274. goog.async.Deferred.prototype.check_ = function() {
  275. if (this.hasFired()) {
  276. if (!this.silentlyCanceled_) {
  277. throw new goog.async.Deferred.AlreadyCalledError(this);
  278. }
  279. this.silentlyCanceled_ = false;
  280. }
  281. };
  282. /**
  283. * Fire the execution sequence for this Deferred by passing the starting result
  284. * to the first registered callback.
  285. * @param {VALUE=} opt_result The starting result.
  286. */
  287. goog.async.Deferred.prototype.callback = function(opt_result) {
  288. this.check_();
  289. this.assertNotDeferred_(opt_result);
  290. this.updateResult_(true /* isSuccess */, opt_result);
  291. };
  292. /**
  293. * Fire the execution sequence for this Deferred by passing the starting error
  294. * result to the first registered errback.
  295. * @param {*=} opt_result The starting error.
  296. */
  297. goog.async.Deferred.prototype.errback = function(opt_result) {
  298. this.check_();
  299. this.assertNotDeferred_(opt_result);
  300. this.makeStackTraceLong_(opt_result);
  301. this.updateResult_(false /* isSuccess */, opt_result);
  302. };
  303. /**
  304. * Attempt to make the error's stack trace be long in that it contains the
  305. * stack trace from the point where the deferred was created on top of the
  306. * current stack trace to give additional context.
  307. * @param {*} error
  308. * @private
  309. */
  310. goog.async.Deferred.prototype.makeStackTraceLong_ = function(error) {
  311. if (!goog.async.Deferred.LONG_STACK_TRACES) {
  312. return;
  313. }
  314. if (this.constructorStack_ && goog.isObject(error) && error.stack &&
  315. // Stack looks like it was system generated. See
  316. // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
  317. (/^[^\n]+(\n [^\n]+)+/).test(error.stack)) {
  318. error.stack = error.stack + '\nDEFERRED OPERATION:\n' +
  319. this.constructorStack_;
  320. }
  321. };
  322. /**
  323. * Asserts that an object is not a Deferred.
  324. * @param {*} obj The object to test.
  325. * @throws {Error} Throws an exception if the object is a Deferred.
  326. * @private
  327. */
  328. goog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {
  329. goog.asserts.assert(
  330. !(obj instanceof goog.async.Deferred),
  331. 'An execution sequence may not be initiated with a blocking Deferred.');
  332. };
  333. /**
  334. * Register a callback function to be called with a successful result. If no
  335. * value is returned by the callback function, the result value is unchanged. If
  336. * a new value is returned, it becomes the Deferred result and will be passed to
  337. * the next callback in the execution sequence.
  338. *
  339. * If the function throws an error, the error becomes the new result and will be
  340. * passed to the next errback in the execution chain.
  341. *
  342. * If the function returns a Deferred, the execution sequence will be blocked
  343. * until that Deferred fires. Its result will be passed to the next callback (or
  344. * errback if it is an error result) in this Deferred's execution sequence.
  345. *
  346. * @param {function(this:T,VALUE):?} cb The function to be called with a
  347. * successful result.
  348. * @param {T=} opt_scope An optional scope to call the callback in.
  349. * @return {!goog.async.Deferred} This Deferred.
  350. * @template T
  351. */
  352. goog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {
  353. return this.addCallbacks(cb, null, opt_scope);
  354. };
  355. /**
  356. * Register a callback function to be called with an error result. If no value
  357. * is returned by the function, the error result is unchanged. If a new error
  358. * value is returned or thrown, that error becomes the Deferred result and will
  359. * be passed to the next errback in the execution sequence.
  360. *
  361. * If the errback function handles the error by returning a non-error value,
  362. * that result will be passed to the next normal callback in the sequence.
  363. *
  364. * If the function returns a Deferred, the execution sequence will be blocked
  365. * until that Deferred fires. Its result will be passed to the next callback (or
  366. * errback if it is an error result) in this Deferred's execution sequence.
  367. *
  368. * @param {function(this:T,?):?} eb The function to be called on an
  369. * unsuccessful result.
  370. * @param {T=} opt_scope An optional scope to call the errback in.
  371. * @return {!goog.async.Deferred<VALUE>} This Deferred.
  372. * @template T
  373. */
  374. goog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {
  375. return this.addCallbacks(null, eb, opt_scope);
  376. };
  377. /**
  378. * Registers one function as both a callback and errback.
  379. *
  380. * @param {function(this:T,?):?} f The function to be called on any result.
  381. * @param {T=} opt_scope An optional scope to call the function in.
  382. * @return {!goog.async.Deferred} This Deferred.
  383. * @template T
  384. */
  385. goog.async.Deferred.prototype.addBoth = function(f, opt_scope) {
  386. return this.addCallbacks(f, f, opt_scope);
  387. };
  388. /**
  389. * Like addBoth, but propagates uncaught exceptions in the errback.
  390. *
  391. * @param {function(this:T,?):?} f The function to be called on any result.
  392. * @param {T=} opt_scope An optional scope to call the function in.
  393. * @return {!goog.async.Deferred<VALUE>} This Deferred.
  394. * @template T
  395. */
  396. goog.async.Deferred.prototype.addFinally = function(f, opt_scope) {
  397. return this.addCallbacks(f, function(err) {
  398. var result = f.call(/** @type {?} */ (this), err);
  399. if (!goog.isDef(result)) {
  400. throw err;
  401. }
  402. return result;
  403. }, opt_scope);
  404. };
  405. /**
  406. * Registers a callback function and an errback function at the same position
  407. * in the execution sequence. Only one of these functions will execute,
  408. * depending on the error state during the execution sequence.
  409. *
  410. * NOTE: This is not equivalent to {@code def.addCallback().addErrback()}! If
  411. * the callback is invoked, the errback will be skipped, and vice versa.
  412. *
  413. * @param {?(function(this:T,VALUE):?)} cb The function to be called on a
  414. * successful result.
  415. * @param {?(function(this:T,?):?)} eb The function to be called on an
  416. * unsuccessful result.
  417. * @param {T=} opt_scope An optional scope to call the functions in.
  418. * @return {!goog.async.Deferred} This Deferred.
  419. * @template T
  420. */
  421. goog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {
  422. goog.asserts.assert(!this.blocking_, 'Blocking Deferreds can not be re-used');
  423. this.sequence_.push([cb, eb, opt_scope]);
  424. if (this.hasFired()) {
  425. this.fire_();
  426. }
  427. return this;
  428. };
  429. /**
  430. * Implements {@see goog.Thenable} for seamless integration with
  431. * {@see goog.Promise}.
  432. * Deferred results are mutable and may represent multiple values over
  433. * their lifetime. Calling {@code then} on a Deferred returns a Promise
  434. * with the result of the Deferred at that point in its callback chain.
  435. * Note that if the Deferred result is never mutated, and only
  436. * {@code then} calls are made, the Deferred will behave like a Promise.
  437. *
  438. * @override
  439. */
  440. goog.async.Deferred.prototype.then = function(opt_onFulfilled, opt_onRejected,
  441. opt_context) {
  442. var resolve, reject;
  443. var promise = new goog.Promise(function(res, rej) {
  444. // Copying resolvers to outer scope, so that they are available when the
  445. // deferred callback fires (which may be synchronous).
  446. resolve = res;
  447. reject = rej;
  448. });
  449. this.addCallbacks(resolve, function(reason) {
  450. if (reason instanceof goog.async.Deferred.CanceledError) {
  451. promise.cancel();
  452. } else {
  453. reject(reason);
  454. }
  455. });
  456. return promise.then(opt_onFulfilled, opt_onRejected, opt_context);
  457. };
  458. goog.Thenable.addImplementation(goog.async.Deferred);
  459. /**
  460. * Links another Deferred to the end of this Deferred's execution sequence. The
  461. * result of this execution sequence will be passed as the starting result for
  462. * the chained Deferred, invoking either its first callback or errback.
  463. *
  464. * @param {!goog.async.Deferred} otherDeferred The Deferred to chain.
  465. * @return {!goog.async.Deferred} This Deferred.
  466. */
  467. goog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {
  468. this.addCallbacks(
  469. otherDeferred.callback, otherDeferred.errback, otherDeferred);
  470. return this;
  471. };
  472. /**
  473. * Makes this Deferred wait for another Deferred's execution sequence to
  474. * complete before continuing.
  475. *
  476. * This is equivalent to adding a callback that returns {@code otherDeferred},
  477. * but doesn't prevent additional callbacks from being added to
  478. * {@code otherDeferred}.
  479. *
  480. * @param {!goog.async.Deferred|!goog.Thenable} otherDeferred The Deferred
  481. * to wait for.
  482. * @return {!goog.async.Deferred} This Deferred.
  483. */
  484. goog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {
  485. if (!(otherDeferred instanceof goog.async.Deferred)) {
  486. // The Thenable case.
  487. return this.addCallback(function() {
  488. return otherDeferred;
  489. });
  490. }
  491. return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));
  492. };
  493. /**
  494. * Creates a branch off this Deferred's execution sequence, and returns it as a
  495. * new Deferred. The branched Deferred's starting result will be shared with the
  496. * parent at the point of the branch, even if further callbacks are added to the
  497. * parent.
  498. *
  499. * All branches at the same stage in the execution sequence will receive the
  500. * same starting value.
  501. *
  502. * @param {boolean=} opt_propagateCancel If cancel() is called on every child
  503. * branch created with opt_propagateCancel, the parent will be canceled as
  504. * well.
  505. * @return {!goog.async.Deferred<VALUE>} A Deferred that will be started with
  506. * the computed result from this stage in the execution sequence.
  507. */
  508. goog.async.Deferred.prototype.branch = function(opt_propagateCancel) {
  509. var d = new goog.async.Deferred();
  510. this.chainDeferred(d);
  511. if (opt_propagateCancel) {
  512. d.parent_ = this;
  513. this.branches_++;
  514. }
  515. return d;
  516. };
  517. /**
  518. * @return {boolean} Whether the execution sequence has been started on this
  519. * Deferred by invoking {@code callback} or {@code errback}.
  520. */
  521. goog.async.Deferred.prototype.hasFired = function() {
  522. return this.fired_;
  523. };
  524. /**
  525. * @param {*} res The latest result in the execution sequence.
  526. * @return {boolean} Whether the current result is an error that should cause
  527. * the next errback to fire. May be overridden by subclasses to handle
  528. * special error types.
  529. * @protected
  530. */
  531. goog.async.Deferred.prototype.isError = function(res) {
  532. return res instanceof Error;
  533. };
  534. /**
  535. * @return {boolean} Whether an errback exists in the remaining sequence.
  536. * @private
  537. */
  538. goog.async.Deferred.prototype.hasErrback_ = function() {
  539. return goog.array.some(this.sequence_, function(sequenceRow) {
  540. // The errback is the second element in the array.
  541. return goog.isFunction(sequenceRow[1]);
  542. });
  543. };
  544. /**
  545. * Exhausts the execution sequence while a result is available. The result may
  546. * be modified by callbacks or errbacks, and execution will block if the
  547. * returned result is an incomplete Deferred.
  548. *
  549. * @private
  550. */
  551. goog.async.Deferred.prototype.fire_ = function() {
  552. if (this.unhandledErrorId_ && this.hasFired() && this.hasErrback_()) {
  553. // It is possible to add errbacks after the Deferred has fired. If a new
  554. // errback is added immediately after the Deferred encountered an unhandled
  555. // error, but before that error is rethrown, the error is unscheduled.
  556. goog.async.Deferred.unscheduleError_(this.unhandledErrorId_);
  557. this.unhandledErrorId_ = 0;
  558. }
  559. if (this.parent_) {
  560. this.parent_.branches_--;
  561. delete this.parent_;
  562. }
  563. var res = this.result_;
  564. var unhandledException = false;
  565. var isNewlyBlocked = false;
  566. while (this.sequence_.length && !this.blocked_) {
  567. var sequenceEntry = this.sequence_.shift();
  568. var callback = sequenceEntry[0];
  569. var errback = sequenceEntry[1];
  570. var scope = sequenceEntry[2];
  571. var f = this.hadError_ ? errback : callback;
  572. if (f) {
  573. try {
  574. var ret = f.call(scope || this.defaultScope_, res);
  575. // If no result, then use previous result.
  576. if (goog.isDef(ret)) {
  577. // Bubble up the error as long as the return value hasn't changed.
  578. this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));
  579. this.result_ = res = ret;
  580. }
  581. if (goog.Thenable.isImplementedBy(res) ||
  582. (typeof goog.global['Promise'] === 'function' &&
  583. res instanceof goog.global['Promise'])) {
  584. isNewlyBlocked = true;
  585. this.blocked_ = true;
  586. }
  587. } catch (ex) {
  588. res = ex;
  589. this.hadError_ = true;
  590. this.makeStackTraceLong_(res);
  591. if (!this.hasErrback_()) {
  592. // If an error is thrown with no additional errbacks in the queue,
  593. // prepare to rethrow the error.
  594. unhandledException = true;
  595. }
  596. }
  597. }
  598. }
  599. this.result_ = res;
  600. if (isNewlyBlocked) {
  601. var onCallback = goog.bind(this.continue_, this, true /* isSuccess */);
  602. var onErrback = goog.bind(this.continue_, this, false /* isSuccess */);
  603. if (res instanceof goog.async.Deferred) {
  604. res.addCallbacks(onCallback, onErrback);
  605. res.blocking_ = true;
  606. } else {
  607. res.then(onCallback, onErrback);
  608. }
  609. } else if (goog.async.Deferred.STRICT_ERRORS && this.isError(res) &&
  610. !(res instanceof goog.async.Deferred.CanceledError)) {
  611. this.hadError_ = true;
  612. unhandledException = true;
  613. }
  614. if (unhandledException) {
  615. // Rethrow the unhandled error after a timeout. Execution will continue, but
  616. // the error will be seen by global handlers and the user. The throw will
  617. // be canceled if another errback is appended before the timeout executes.
  618. // The error's original stack trace is preserved where available.
  619. this.unhandledErrorId_ = goog.async.Deferred.scheduleError_(res);
  620. }
  621. };
  622. /**
  623. * Creates a Deferred that has an initial result.
  624. *
  625. * @param {*=} opt_result The result.
  626. * @return {!goog.async.Deferred} The new Deferred.
  627. */
  628. goog.async.Deferred.succeed = function(opt_result) {
  629. var d = new goog.async.Deferred();
  630. d.callback(opt_result);
  631. return d;
  632. };
  633. /**
  634. * Creates a Deferred that fires when the given promise resolves.
  635. * Use only during migration to Promises.
  636. *
  637. * @param {!goog.Promise<T>} promise
  638. * @return {!goog.async.Deferred<T>} The new Deferred.
  639. * @template T
  640. */
  641. goog.async.Deferred.fromPromise = function(promise) {
  642. var d = new goog.async.Deferred();
  643. d.callback();
  644. d.addCallback(function() {
  645. return promise;
  646. });
  647. return d;
  648. };
  649. /**
  650. * Creates a Deferred that has an initial error result.
  651. *
  652. * @param {*} res The error result.
  653. * @return {!goog.async.Deferred} The new Deferred.
  654. */
  655. goog.async.Deferred.fail = function(res) {
  656. var d = new goog.async.Deferred();
  657. d.errback(res);
  658. return d;
  659. };
  660. /**
  661. * Creates a Deferred that has already been canceled.
  662. *
  663. * @return {!goog.async.Deferred} The new Deferred.
  664. */
  665. goog.async.Deferred.canceled = function() {
  666. var d = new goog.async.Deferred();
  667. d.cancel();
  668. return d;
  669. };
  670. /**
  671. * Normalizes values that may or may not be Deferreds.
  672. *
  673. * If the input value is a Deferred, the Deferred is branched (so the original
  674. * execution sequence is not modified) and the input callback added to the new
  675. * branch. The branch is returned to the caller.
  676. *
  677. * If the input value is not a Deferred, the callback will be executed
  678. * immediately and an already firing Deferred will be returned to the caller.
  679. *
  680. * In the following (contrived) example, if <code>isImmediate</code> is true
  681. * then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.
  682. *
  683. * <pre>
  684. * var value;
  685. * if (isImmediate) {
  686. * value = 3;
  687. * } else {
  688. * value = new goog.async.Deferred();
  689. * setTimeout(function() { value.callback(6); }, 2000);
  690. * }
  691. *
  692. * var d = goog.async.Deferred.when(value, alert);
  693. * </pre>
  694. *
  695. * @param {*} value Deferred or normal value to pass to the callback.
  696. * @param {function(this:T, ?):?} callback The callback to execute.
  697. * @param {T=} opt_scope An optional scope to call the callback in.
  698. * @return {!goog.async.Deferred} A new Deferred that will call the input
  699. * callback with the input value.
  700. * @template T
  701. */
  702. goog.async.Deferred.when = function(value, callback, opt_scope) {
  703. if (value instanceof goog.async.Deferred) {
  704. return value.branch(true).addCallback(callback, opt_scope);
  705. } else {
  706. return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);
  707. }
  708. };
  709. /**
  710. * An error sub class that is used when a Deferred has already been called.
  711. * @param {!goog.async.Deferred} deferred The Deferred.
  712. *
  713. * @constructor
  714. * @extends {goog.debug.Error}
  715. */
  716. goog.async.Deferred.AlreadyCalledError = function(deferred) {
  717. goog.debug.Error.call(this);
  718. /**
  719. * The Deferred that raised this error.
  720. * @type {goog.async.Deferred}
  721. */
  722. this.deferred = deferred;
  723. };
  724. goog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);
  725. /** @override */
  726. goog.async.Deferred.AlreadyCalledError.prototype.message =
  727. 'Deferred has already fired';
  728. /** @override */
  729. goog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';
  730. /**
  731. * An error sub class that is used when a Deferred is canceled.
  732. *
  733. * @param {!goog.async.Deferred} deferred The Deferred object.
  734. * @constructor
  735. * @extends {goog.debug.Error}
  736. */
  737. goog.async.Deferred.CanceledError = function(deferred) {
  738. goog.debug.Error.call(this);
  739. /**
  740. * The Deferred that raised this error.
  741. * @type {goog.async.Deferred}
  742. */
  743. this.deferred = deferred;
  744. };
  745. goog.inherits(goog.async.Deferred.CanceledError, goog.debug.Error);
  746. /** @override */
  747. goog.async.Deferred.CanceledError.prototype.message = 'Deferred was canceled';
  748. /** @override */
  749. goog.async.Deferred.CanceledError.prototype.name = 'CanceledError';
  750. /**
  751. * Wrapper around errors that are scheduled to be thrown by failing deferreds
  752. * after a timeout.
  753. *
  754. * @param {*} error Error from a failing deferred.
  755. * @constructor
  756. * @final
  757. * @private
  758. * @struct
  759. */
  760. goog.async.Deferred.Error_ = function(error) {
  761. /** @const @private {number} */
  762. this.id_ = goog.global.setTimeout(goog.bind(this.throwError, this), 0);
  763. /** @const @private {*} */
  764. this.error_ = error;
  765. };
  766. /**
  767. * Actually throws the error and removes it from the list of pending
  768. * deferred errors.
  769. */
  770. goog.async.Deferred.Error_.prototype.throwError = function() {
  771. goog.asserts.assert(goog.async.Deferred.errorMap_[this.id_],
  772. 'Cannot throw an error that is not scheduled.');
  773. delete goog.async.Deferred.errorMap_[this.id_];
  774. throw this.error_;
  775. };
  776. /**
  777. * Resets the error throw timer.
  778. */
  779. goog.async.Deferred.Error_.prototype.resetTimer = function() {
  780. goog.global.clearTimeout(this.id_);
  781. };
  782. /**
  783. * Map of unhandled errors scheduled to be rethrown in a future timestep.
  784. * @private {!Object<(number|string), goog.async.Deferred.Error_>}
  785. */
  786. goog.async.Deferred.errorMap_ = {};
  787. /**
  788. * Schedules an error to be thrown after a delay.
  789. * @param {*} error Error from a failing deferred.
  790. * @return {number} Id of the error.
  791. * @private
  792. */
  793. goog.async.Deferred.scheduleError_ = function(error) {
  794. var deferredError = new goog.async.Deferred.Error_(error);
  795. goog.async.Deferred.errorMap_[deferredError.id_] = deferredError;
  796. return deferredError.id_;
  797. };
  798. /**
  799. * Unschedules an error from being thrown.
  800. * @param {number} id Id of the deferred error to unschedule.
  801. * @private
  802. */
  803. goog.async.Deferred.unscheduleError_ = function(id) {
  804. var error = goog.async.Deferred.errorMap_[id];
  805. if (error) {
  806. error.resetTimer();
  807. delete goog.async.Deferred.errorMap_[id];
  808. }
  809. };
  810. /**
  811. * Asserts that there are no pending deferred errors. If there are any
  812. * scheduled errors, one will be thrown immediately to make this function fail.
  813. */
  814. goog.async.Deferred.assertNoErrors = function() {
  815. var map = goog.async.Deferred.errorMap_;
  816. for (var key in map) {
  817. var error = map[key];
  818. error.resetTimer();
  819. error.throwError();
  820. }
  821. };