deferredlist.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2005 Bob Ippolito. All Rights Reserved.
  2. // Modifications Copyright 2009 The Closure Library Authors.
  3. // All Rights Reserved.
  4. /**
  5. * Portions of this code are from MochiKit, received by The Closure
  6. * Library Authors under the MIT license. All other code is Copyright
  7. * 2005-2009 The Closure Library Authors. All Rights Reserved.
  8. */
  9. /**
  10. * @fileoverview Class for tracking multiple asynchronous operations and
  11. * handling the results. The DeferredList object here is patterned after the
  12. * DeferredList object in the Twisted python networking framework.
  13. *
  14. * Based on the MochiKit code.
  15. *
  16. * See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
  17. *
  18. * @author brenneman@google.com (Shawn Brenneman)
  19. */
  20. goog.provide('goog.async.DeferredList');
  21. goog.require('goog.async.Deferred');
  22. /**
  23. * Constructs an object that waits on the results of multiple asynchronous
  24. * operations and marshals the results. It is itself a <code>Deferred</code>,
  25. * and may have an execution sequence of callback functions added to it. Each
  26. * <code>DeferredList</code> instance is single use and may be fired only once.
  27. *
  28. * The default behavior of a <code>DeferredList</code> is to wait for a success
  29. * or error result from every <code>Deferred</code> in its input list. Once
  30. * every result is available, the <code>DeferredList</code>'s execution sequence
  31. * is fired with a list of <code>[success, result]</code> array pairs, where
  32. * <code>success</code> is a boolean indicating whether <code>result</code> was
  33. * the product of a callback or errback. The list's completion criteria and
  34. * result list may be modified by setting one or more of the boolean options
  35. * documented below.
  36. *
  37. * <code>Deferred</code> instances passed into a <code>DeferredList</code> are
  38. * independent, and may have additional callbacks and errbacks added to their
  39. * execution sequences after they are passed as inputs to the list.
  40. *
  41. * @param {!Array<!goog.async.Deferred>} list An array of deferred results to
  42. * wait for.
  43. * @param {boolean=} opt_fireOnOneCallback Whether to stop waiting as soon as
  44. * one input completes successfully. In this case, the
  45. * <code>DeferredList</code>'s callback chain will be called with a two
  46. * element array, <code>[index, result]</code>, where <code>index</code>
  47. * identifies which input <code>Deferred</code> produced the successful
  48. * <code>result</code>.
  49. * @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one
  50. * input reports an error. The failing result is passed to the
  51. * <code>DeferredList</code>'s errback sequence.
  52. * @param {boolean=} opt_consumeErrors When true, any errors fired by a
  53. * <code>Deferred</code> in the input list will be captured and replaced
  54. * with a succeeding null result. Any callbacks added to the
  55. * <code>Deferred</code> after its use in the <code>DeferredList</code> will
  56. * receive null instead of the error.
  57. * @param {Function=} opt_canceler A function that will be called if the
  58. * <code>DeferredList</code> is canceled. @see goog.async.Deferred#cancel
  59. * @param {Object=} opt_defaultScope The default scope to invoke callbacks or
  60. * errbacks in.
  61. * @constructor
  62. * @extends {goog.async.Deferred}
  63. */
  64. goog.async.DeferredList = function(
  65. list, opt_fireOnOneCallback, opt_fireOnOneErrback, opt_consumeErrors,
  66. opt_canceler, opt_defaultScope) {
  67. goog.async.DeferredList.base(this, 'constructor',
  68. opt_canceler, opt_defaultScope);
  69. /**
  70. * The list of Deferred objects to wait for.
  71. * @const {!Array<!goog.async.Deferred>}
  72. * @private
  73. */
  74. this.list_ = list;
  75. /**
  76. * The stored return values of the Deferred objects.
  77. * @const {!Array}
  78. * @private
  79. */
  80. this.deferredResults_ = [];
  81. /**
  82. * Whether to fire on the first successful callback instead of waiting for
  83. * every Deferred to complete.
  84. * @const {boolean}
  85. * @private
  86. */
  87. this.fireOnOneCallback_ = !!opt_fireOnOneCallback;
  88. /**
  89. * Whether to fire on the first error result received instead of waiting for
  90. * every Deferred to complete.
  91. * @const {boolean}
  92. * @private
  93. */
  94. this.fireOnOneErrback_ = !!opt_fireOnOneErrback;
  95. /**
  96. * Whether to stop error propagation on the input Deferred objects. If the
  97. * DeferredList sees an error from one of the Deferred inputs, the error will
  98. * be captured, and the Deferred will be returned to success state with a null
  99. * return value.
  100. * @const {boolean}
  101. * @private
  102. */
  103. this.consumeErrors_ = !!opt_consumeErrors;
  104. /**
  105. * The number of input deferred objects that have fired.
  106. * @private {number}
  107. */
  108. this.numFinished_ = 0;
  109. for (var i = 0; i < list.length; i++) {
  110. var d = list[i];
  111. d.addCallbacks(goog.bind(this.handleCallback_, this, i, true),
  112. goog.bind(this.handleCallback_, this, i, false));
  113. }
  114. if (list.length == 0 && !this.fireOnOneCallback_) {
  115. this.callback(this.deferredResults_);
  116. }
  117. };
  118. goog.inherits(goog.async.DeferredList, goog.async.Deferred);
  119. /**
  120. * Registers the result from an input deferred callback or errback. The result
  121. * is returned and may be passed to additional handlers in the callback chain.
  122. *
  123. * @param {number} index The index of the firing deferred object in the input
  124. * list.
  125. * @param {boolean} success Whether the result is from a callback or errback.
  126. * @param {*} result The result of the callback or errback.
  127. * @return {*} The result, to be handled by the next handler in the deferred's
  128. * callback chain (if any). If consumeErrors is set, an error result is
  129. * replaced with null.
  130. * @private
  131. */
  132. goog.async.DeferredList.prototype.handleCallback_ = function(
  133. index, success, result) {
  134. this.numFinished_++;
  135. this.deferredResults_[index] = [success, result];
  136. if (!this.hasFired()) {
  137. if (this.fireOnOneCallback_ && success) {
  138. this.callback([index, result]);
  139. } else if (this.fireOnOneErrback_ && !success) {
  140. this.errback(result);
  141. } else if (this.numFinished_ == this.list_.length) {
  142. this.callback(this.deferredResults_);
  143. }
  144. }
  145. if (this.consumeErrors_ && !success) {
  146. result = null;
  147. }
  148. return result;
  149. };
  150. /** @override */
  151. goog.async.DeferredList.prototype.errback = function(res) {
  152. goog.async.DeferredList.base(this, 'errback', res);
  153. // On error, cancel any pending requests.
  154. for (var i = 0; i < this.list_.length; i++) {
  155. this.list_[i].cancel();
  156. }
  157. };
  158. /**
  159. * Creates a <code>DeferredList</code> that gathers results from multiple
  160. * <code>Deferred</code> inputs. If all inputs succeed, the callback is fired
  161. * with the list of results as a flat array. If any input fails, the list's
  162. * errback is fired immediately with the offending error, and all other pending
  163. * inputs are canceled.
  164. *
  165. * @param {!Array<!goog.async.Deferred>} list The list of <code>Deferred</code>
  166. * inputs to wait for.
  167. * @return {!goog.async.Deferred} The deferred list of results from the inputs
  168. * if they all succeed, or the error result of the first input to fail.
  169. */
  170. goog.async.DeferredList.gatherResults = function(list) {
  171. return new goog.async.DeferredList(list, false, true).
  172. addCallback(function(results) {
  173. var output = [];
  174. for (var i = 0; i < results.length; i++) {
  175. output[i] = results[i][1];
  176. }
  177. return output;
  178. });
  179. };