aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/closure-library/third_party/closure/goog/mochikit/async/deferredlist.js
blob: 4180906ca11ab1327e27a9314074ee2b77e1a9d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// 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.array');
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 sends results to its registered callback chain. Each instance is single
 * use and may only fire once.
 *
 * Unless overridden by one of the options below, the <code>DeferredList</code>
 * will wait for a result from every input <code>Deferred</code>. The results
 * are stored in a list of two-element arrays as <code>[success, result]</code>,
 * where <code>success</code> is whether that result was from a callback or
 * errback. Once all results are available, the <code>DeferredList</code>'s
 * callback chain is invoked with the full result list.
 *
 * @param {!Array.<!goog.async.Deferred>} list An array of deferred objects 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
 *     <code>result</code>.
 * @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one
 *     input reports an error. The error result is passed to the
 *     <code>DeferredList</code>'s error callback chain.
 * @param {boolean=} opt_consumeErrors When true, will stop propagation of the
 *     error callback chain for input deferred objects. If the failing deferred
 *     has a registered callback after this <code>DeferredList</code>, it will
 *     be called with null instead of an <code>Error</code>.
 * @param {Function=} opt_canceller A function that will be called if the
 *     deferred list is canceled.
 * @param {Object=} opt_defaultScope The default scope to call callbacks with.
 * @constructor
 * @extends {goog.async.Deferred}
 */
goog.async.DeferredList = function(list,
                                   opt_fireOnOneCallback,
                                   opt_fireOnOneErrback,
                                   opt_consumeErrors,
                                   opt_canceller,
                                   opt_defaultScope) {
  goog.async.Deferred.call(this, opt_canceller, opt_defaultScope);

  /**
   * The list of Deferred objects to wait for.
   * @type {!Array.<!goog.async.Deferred>}
   * @private
   */
  this.list_ = list;

  /**
   * The stored return values of the Deferred objects.
   * @type {!Array}
   * @private
   */
  this.deferredResults_ = [];

  /**
   * Whether to fire on the first successful callback instead of waiting for
   * every Deferred to complete.
   * @type {boolean}
   * @private
   */
  this.fireOnOneCallback_ = !!opt_fireOnOneCallback;

  /**
   * Whether to fire on the first error result received instead of waiting for
   * every Deferred to complete.
   * @type {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.
   * @type {boolean}
   * @private
   */
  this.consumeErrors_ = !!opt_consumeErrors;

  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);


/**
 * The number of input deferred objects that have fired.
 * @type {number}
 * @private
 */
goog.async.DeferredList.prototype.numFinished_ = 0;


/**
 * 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;
};


/** @inheritDoc */
goog.async.DeferredList.prototype.errback = function(res) {
  goog.async.DeferredList.superClass_.errback.call(this, res);
  // On error, cancel any pending requests.
  goog.array.forEach(this.list_, function(item) {
    item.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 errback is
 * fired with the error.
 *
 * @param {!Array.<!goog.async.Deferred>} list The list of deferred objects to
 *     wait for.
 * @return {!goog.async.DeferredList} A new deferred list.
 */
goog.async.DeferredList.gatherResults = function(list) {
  var d = new goog.async.DeferredList(list, false, true);

  d.addCallback(function(results) {
    return goog.array.map(results, function(res) {
      return res[1];
    });
  });

  return d;
};