OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart.async; | 5 part of dart.async; |
6 | 6 |
7 /** The onValue and onError handlers return either a value or a future */ | 7 /** The onValue and onError handlers return either a value or a future */ |
8 typedef dynamic _FutureOnValue<T>(T value); | 8 typedef dynamic _FutureOnValue<T>(T value); |
9 /** Test used by [Future.catchError] to handle skip some errors. */ | 9 /** Test used by [Future.catchError] to handle skip some errors. */ |
10 typedef bool _FutureErrorTest(var error); | 10 typedef bool _FutureErrorTest(var error); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 _FutureListener.catchError(this.result, | 88 _FutureListener.catchError(this.result, |
89 this.errorCallback, _FutureErrorTest test) | 89 this.errorCallback, _FutureErrorTest test) |
90 : callback = test, | 90 : callback = test, |
91 state = (test == null) ? STATE_CATCHERROR : STATE_CATCHERROR_TEST; | 91 state = (test == null) ? STATE_CATCHERROR : STATE_CATCHERROR_TEST; |
92 | 92 |
93 _FutureListener.whenComplete(this.result, _FutureAction onComplete) | 93 _FutureListener.whenComplete(this.result, _FutureAction onComplete) |
94 : callback = onComplete, | 94 : callback = onComplete, |
95 errorCallback = null, | 95 errorCallback = null, |
96 state = STATE_WHENCOMPLETE; | 96 state = STATE_WHENCOMPLETE; |
97 | 97 |
98 _FutureListener.chain(this.result) | |
99 : callback = null, | |
100 errorCallback = null, | |
101 state = STATE_CHAIN; | |
102 | |
103 Zone get _zone => result._zone; | 98 Zone get _zone => result._zone; |
104 | 99 |
105 bool get handlesValue => (state & MASK_VALUE != 0); | 100 bool get handlesValue => (state & MASK_VALUE != 0); |
106 bool get handlesError => (state & MASK_ERROR != 0); | 101 bool get handlesError => (state & MASK_ERROR != 0); |
107 bool get hasErrorTest => (state == STATE_CATCHERROR_TEST); | 102 bool get hasErrorTest => (state == STATE_CATCHERROR_TEST); |
108 bool get handlesComplete => (state == STATE_WHENCOMPLETE); | 103 bool get handlesComplete => (state == STATE_WHENCOMPLETE); |
109 | 104 |
110 _FutureOnValue get _onValue { | 105 _FutureOnValue get _onValue { |
111 assert(handlesValue); | 106 assert(handlesValue); |
112 return callback; | 107 return callback; |
(...skipping 13 matching lines...) Expand all Loading... |
126 /// Initial state, waiting for a result. In this state, the | 121 /// Initial state, waiting for a result. In this state, the |
127 /// [resultOrListeners] field holds a single-linked list of | 122 /// [resultOrListeners] field holds a single-linked list of |
128 /// [_FutureListener] listeners. | 123 /// [_FutureListener] listeners. |
129 static const int _INCOMPLETE = 0; | 124 static const int _INCOMPLETE = 0; |
130 /// Pending completion. Set when completed using [_asyncComplete] or | 125 /// Pending completion. Set when completed using [_asyncComplete] or |
131 /// [_asyncCompleteError]. It is an error to try to complete it again. | 126 /// [_asyncCompleteError]. It is an error to try to complete it again. |
132 /// [resultOrListeners] holds listeners. | 127 /// [resultOrListeners] holds listeners. |
133 static const int _PENDING_COMPLETE = 1; | 128 static const int _PENDING_COMPLETE = 1; |
134 /// The future has been chained to another future. The result of that | 129 /// The future has been chained to another future. The result of that |
135 /// other future becomes the result of this future as well. | 130 /// other future becomes the result of this future as well. |
136 // TODO(floitsch): we don't really need a special "_CHAINED" state. We could | 131 /// [resultOrListeners] contains the source future. |
137 // just use the PENDING_COMPLETE state instead. | |
138 static const int _CHAINED = 2; | 132 static const int _CHAINED = 2; |
139 /// The future has been completed with a value result. | 133 /// The future has been completed with a value result. |
140 static const int _VALUE = 4; | 134 static const int _VALUE = 4; |
141 /// The future has been completed with an error result. | 135 /// The future has been completed with an error result. |
142 static const int _ERROR = 8; | 136 static const int _ERROR = 8; |
143 | 137 |
144 /** Whether the future is complete, and as what. */ | 138 /** Whether the future is complete, and as what. */ |
145 int _state = _INCOMPLETE; | 139 int _state = _INCOMPLETE; |
146 | 140 |
147 /** | 141 /** |
(...skipping 12 matching lines...) Expand all Loading... |
160 * A result is only stored when the future has completed. | 154 * A result is only stored when the future has completed. |
161 * | 155 * |
162 * The listeners is an internally linked list of [_FutureListener]s. | 156 * The listeners is an internally linked list of [_FutureListener]s. |
163 * Listeners are only remembered while the future is not yet complete, | 157 * Listeners are only remembered while the future is not yet complete, |
164 * and it is not chained to another future. | 158 * and it is not chained to another future. |
165 * | 159 * |
166 * The future is another future that his future is chained to. This future | 160 * The future is another future that his future is chained to. This future |
167 * is waiting for the other future to complete, and when it does, this future | 161 * is waiting for the other future to complete, and when it does, this future |
168 * will complete with the same result. | 162 * will complete with the same result. |
169 * All listeners are forwarded to the other future. | 163 * All listeners are forwarded to the other future. |
170 * | |
171 * The cases are disjoint - incomplete and unchained ([_INCOMPLETE]), | |
172 * incomplete and chained ([_CHAINED]), or completed with value or error | |
173 * ([_VALUE] or [_ERROR]) - so the field only needs to hold | |
174 * one value at a time. | |
175 */ | 164 */ |
176 var _resultOrListeners; | 165 var _resultOrListeners; |
177 | 166 |
178 // This constructor is used by async/await. | 167 // This constructor is used by async/await. |
179 _Future(); | 168 _Future(); |
180 | 169 |
181 /// Valid types for value: `T` or `Future<T>`. | 170 /// Valid types for value: `T` or `Future<T>`. |
182 _Future.immediate(value) { | 171 _Future.immediate(value) { |
183 _asyncComplete(value); | 172 _asyncComplete(value); |
184 } | 173 } |
185 | 174 |
186 _Future.immediateError(var error, [StackTrace stackTrace]) { | 175 _Future.immediateError(var error, [StackTrace stackTrace]) { |
187 _asyncCompleteError(error, stackTrace); | 176 _asyncCompleteError(error, stackTrace); |
188 } | 177 } |
189 | 178 |
190 bool get _mayComplete => _state == _INCOMPLETE; | 179 bool get _mayComplete => _state == _INCOMPLETE; |
191 bool get _isPendingComplete => _state == _PENDING_COMPLETE; | 180 bool get _isPendingComplete => _state == _PENDING_COMPLETE; |
| 181 bool get _mayAddListener => _state <= _PENDING_COMPLETE; |
192 bool get _isChained => _state == _CHAINED; | 182 bool get _isChained => _state == _CHAINED; |
193 bool get _isComplete => _state >= _VALUE; | 183 bool get _isComplete => _state >= _VALUE; |
194 bool get _hasValue => _state == _VALUE; | |
195 bool get _hasError => _state == _ERROR; | 184 bool get _hasError => _state == _ERROR; |
196 | 185 |
197 void _setChained() { | 186 void _setChained(_Future source) { |
198 assert(!_isComplete); | 187 assert(_mayAddListener); |
199 _state = _CHAINED; | 188 _state = _CHAINED; |
| 189 _resultOrListeners = source; |
200 } | 190 } |
201 | 191 |
202 Future then(f(T value), { Function onError }) { | 192 Future then(f(T value), { Function onError }) { |
203 Zone currentZone = Zone.current; | 193 Zone currentZone = Zone.current; |
204 if (!identical(currentZone, _ROOT_ZONE)) { | 194 if (!identical(currentZone, _ROOT_ZONE)) { |
205 f = currentZone.registerUnaryCallback(f); | 195 f = currentZone.registerUnaryCallback(f); |
206 if (onError != null) { | 196 if (onError != null) { |
207 onError = _registerErrorHandler(onError, currentZone); | 197 onError = _registerErrorHandler(onError, currentZone); |
208 } | 198 } |
209 } | 199 } |
(...skipping 21 matching lines...) Expand all Loading... |
231 _Future result = new _Future<T>(); | 221 _Future result = new _Future<T>(); |
232 if (!identical(result._zone, _ROOT_ZONE)) { | 222 if (!identical(result._zone, _ROOT_ZONE)) { |
233 action = result._zone.registerCallback(action); | 223 action = result._zone.registerCallback(action); |
234 } | 224 } |
235 _addListener(new _FutureListener.whenComplete(result, action)); | 225 _addListener(new _FutureListener.whenComplete(result, action)); |
236 return result; | 226 return result; |
237 } | 227 } |
238 | 228 |
239 Stream<T> asStream() => new Stream<T>.fromFuture(this); | 229 Stream<T> asStream() => new Stream<T>.fromFuture(this); |
240 | 230 |
241 void _markPendingCompletion() { | 231 void _setPendingComplete() { |
242 if (!_mayComplete) throw new StateError("Future already completed"); | 232 assert(_mayComplete); |
243 _state = _PENDING_COMPLETE; | 233 _state = _PENDING_COMPLETE; |
244 } | 234 } |
245 | 235 |
246 T get _value { | 236 AsyncError get _error { |
247 assert(_isComplete && _hasValue); | 237 assert(_hasError); |
248 return _resultOrListeners; | 238 return _resultOrListeners; |
249 } | 239 } |
250 | 240 |
251 AsyncError get _error { | 241 _Future get _chainSource { |
252 assert(_isComplete && _hasError); | 242 assert(_isChained); |
253 return _resultOrListeners; | 243 return _resultOrListeners; |
254 } | 244 } |
255 | 245 |
256 // This method is used by async/await. | 246 // This method is used by async/await. |
257 void _setValue(T value) { | 247 void _setValue(T value) { |
258 assert(!_isComplete); // But may have a completion pending. | 248 assert(!_isComplete); // But may have a completion pending. |
259 _state = _VALUE; | 249 _state = _VALUE; |
260 _resultOrListeners = value; | 250 _resultOrListeners = value; |
261 } | 251 } |
262 | 252 |
263 void _setErrorObject(AsyncError error) { | 253 void _setErrorObject(AsyncError error) { |
264 assert(!_isComplete); // But may have a completion pending. | 254 assert(!_isComplete); // But may have a completion pending. |
265 _state = _ERROR; | 255 _state = _ERROR; |
266 _resultOrListeners = error; | 256 _resultOrListeners = error; |
267 } | 257 } |
268 | 258 |
269 void _setError(Object error, StackTrace stackTrace) { | 259 void _setError(Object error, StackTrace stackTrace) { |
270 _setErrorObject(new AsyncError(error, stackTrace)); | 260 _setErrorObject(new AsyncError(error, stackTrace)); |
271 } | 261 } |
272 | 262 |
| 263 /// Copy the completion result of [source] into this future. |
| 264 /// |
| 265 /// Used when a chained future notices that its source is completed. |
| 266 void _cloneResult(_Future source) { |
| 267 assert(!_isComplete); |
| 268 assert(source._isComplete); |
| 269 _state = source._state; |
| 270 _resultOrListeners = source._resultOrListeners; |
| 271 } |
| 272 |
273 void _addListener(_FutureListener listener) { | 273 void _addListener(_FutureListener listener) { |
274 assert(listener._nextListener == null); | 274 assert(listener._nextListener == null); |
275 if (_isComplete) { | 275 if (_mayAddListener) { |
| 276 listener._nextListener = _resultOrListeners; |
| 277 _resultOrListeners = listener; |
| 278 } else { |
| 279 if (_isChained) { |
| 280 // Delegate listeners to chained source future. |
| 281 // If the source is complete, instead copy its values and |
| 282 // drop the chaining. |
| 283 _Future source = _chainSource; |
| 284 if (!source._isComplete) { |
| 285 source._addListener(listener); |
| 286 return; |
| 287 } |
| 288 _cloneResult(source); |
| 289 } |
| 290 assert(_isComplete); |
276 // Handle late listeners asynchronously. | 291 // Handle late listeners asynchronously. |
277 _zone.scheduleMicrotask(() { | 292 _zone.scheduleMicrotask(() { |
278 _propagateToListeners(this, listener); | 293 _propagateToListeners(this, listener); |
279 }); | 294 }); |
280 } else { | |
281 listener._nextListener = _resultOrListeners; | |
282 _resultOrListeners = listener; | |
283 } | 295 } |
284 } | 296 } |
285 | 297 |
| 298 void _prependListeners(_FutureListener listeners) { |
| 299 if (listeners == null) return; |
| 300 if (_mayAddListener) { |
| 301 _FutureListener existingListeners = _resultOrListeners; |
| 302 _resultOrListeners = listeners; |
| 303 if (existingListeners != null) { |
| 304 _FutureListener cursor = listeners; |
| 305 while (cursor._nextListener != null) { |
| 306 cursor = cursor._nextListener; |
| 307 } |
| 308 cursor._nextListener = existingListeners; |
| 309 } |
| 310 } else { |
| 311 if (_isChained) { |
| 312 // Delegate listeners to chained source future. |
| 313 // If the source is complete, instead copy its values and |
| 314 // drop the chaining. |
| 315 _Future source = _chainSource; |
| 316 if (!source._isComplete) { |
| 317 source._prependListeners(listeners); |
| 318 return; |
| 319 } |
| 320 _cloneResult(source); |
| 321 } |
| 322 assert(_isComplete); |
| 323 listeners = _reverseListeners(listeners); |
| 324 _zone.scheduleMicrotask(() { |
| 325 _propagateToListeners(this, listeners); |
| 326 }); |
| 327 } |
| 328 } |
| 329 |
286 _FutureListener _removeListeners() { | 330 _FutureListener _removeListeners() { |
287 // Reverse listeners before returning them, so the resulting list is in | 331 // Reverse listeners before returning them, so the resulting list is in |
288 // subscription order. | 332 // subscription order. |
289 assert(!_isComplete); | 333 assert(!_isComplete); |
290 _FutureListener current = _resultOrListeners; | 334 _FutureListener current = _resultOrListeners; |
291 _resultOrListeners = null; | 335 _resultOrListeners = null; |
| 336 return _reverseListeners(current); |
| 337 } |
| 338 |
| 339 _FutureListener _reverseListeners(_FutureListener listeners) { |
292 _FutureListener prev = null; | 340 _FutureListener prev = null; |
| 341 _FutureListener current = listeners; |
293 while (current != null) { | 342 while (current != null) { |
294 _FutureListener next = current._nextListener; | 343 _FutureListener next = current._nextListener; |
295 current._nextListener = prev; | 344 current._nextListener = prev; |
296 prev = current; | 345 prev = current; |
297 current = next; | 346 current = next; |
298 } | 347 } |
299 return prev; | 348 return prev; |
300 } | 349 } |
301 | 350 |
302 // Take the value (when completed) of source and complete target with that | 351 // Take the value (when completed) of source and complete target with that |
303 // value (or error). This function could chain all Futures, but is slower | 352 // value (or error). This function could chain all Futures, but is slower |
304 // for _Future than _chainCoreFuture, so you must use _chainCoreFuture | 353 // for _Future than _chainCoreFuture, so you must use _chainCoreFuture |
305 // in that case. | 354 // in that case. |
306 static void _chainForeignFuture(Future source, _Future target) { | 355 static void _chainForeignFuture(Future source, _Future target) { |
307 assert(!target._isComplete); | 356 assert(!target._isComplete); |
308 assert(source is! _Future); | 357 assert(source is! _Future); |
309 | 358 |
310 // Mark the target as chained (and as such half-completed). | 359 // Mark the target as chained (and as such half-completed). |
311 target._setChained(); | 360 target._setPendingComplete(); |
312 try { | 361 try { |
313 source.then((value) { | 362 source.then((value) { |
314 assert(target._isChained); | 363 assert(target._isPendingComplete); |
315 target._completeWithValue(value); | 364 target._completeWithValue(value); |
316 }, | 365 }, |
317 // TODO(floitsch): eventually we would like to make this non-optional | 366 // TODO(floitsch): eventually we would like to make this non-optional |
318 // and dependent on the listeners of the target future. If none of | 367 // and dependent on the listeners of the target future. If none of |
319 // the target future's listeners want to have the stack trace we don't | 368 // the target future's listeners want to have the stack trace we don't |
320 // need a trace. | 369 // need a trace. |
321 onError: (error, [stackTrace]) { | 370 onError: (error, [stackTrace]) { |
322 assert(target._isChained); | 371 assert(target._isPendingComplete); |
323 target._completeError(error, stackTrace); | 372 target._completeError(error, stackTrace); |
324 }); | 373 }); |
325 } catch (e, s) { | 374 } catch (e, s) { |
326 // This only happens if the `then` call threw synchronously when given | 375 // This only happens if the `then` call threw synchronously when given |
327 // valid arguments. | 376 // valid arguments. |
328 // That requires a non-conforming implementation of the Future interface, | 377 // That requires a non-conforming implementation of the Future interface, |
329 // which should, hopefully, never happen. | 378 // which should, hopefully, never happen. |
330 scheduleMicrotask(() { | 379 scheduleMicrotask(() { |
331 target._completeError(e, s); | 380 target._completeError(e, s); |
332 }); | 381 }); |
333 } | 382 } |
334 } | 383 } |
335 | 384 |
336 // Take the value (when completed) of source and complete target with that | 385 // Take the value (when completed) of source and complete target with that |
337 // value (or error). This function expects that source is a _Future. | 386 // value (or error). This function expects that source is a _Future. |
338 static void _chainCoreFuture(_Future source, _Future target) { | 387 static void _chainCoreFuture(_Future source, _Future target) { |
339 assert(!target._isComplete); | 388 assert(target._mayAddListener); // Not completed, not already chained. |
340 assert(source is _Future); | 389 while (source._isChained) { |
341 | 390 source = source._chainSource; |
342 // Mark the target as chained (and as such half-completed). | 391 } |
343 target._setChained(); | |
344 _FutureListener listener = new _FutureListener.chain(target); | |
345 if (source._isComplete) { | 392 if (source._isComplete) { |
346 _propagateToListeners(source, listener); | 393 _FutureListener listeners = target._removeListeners(); |
| 394 target._cloneResult(source); |
| 395 _propagateToListeners(target, listeners); |
347 } else { | 396 } else { |
348 source._addListener(listener); | 397 _FutureListener listeners = target._resultOrListeners; |
| 398 target._setChained(source); |
| 399 source._prependListeners(listeners); |
349 } | 400 } |
350 } | 401 } |
351 | 402 |
352 void _complete(value) { | 403 void _complete(value) { |
353 assert(!_isComplete); | 404 assert(!_isComplete); |
354 if (value is Future) { | 405 if (value is Future) { |
355 if (value is _Future) { | 406 if (value is _Future) { |
356 _chainCoreFuture(value, this); | 407 _chainCoreFuture(value, this); |
357 } else { | 408 } else { |
358 _chainForeignFuture(value, this); | 409 _chainForeignFuture(value, this); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
397 if (value == null) { | 448 if (value == null) { |
398 // No checks for `null`. | 449 // No checks for `null`. |
399 } else if (value is Future) { | 450 } else if (value is Future) { |
400 // Assign to typed variables so we get earlier checks in checked mode. | 451 // Assign to typed variables so we get earlier checks in checked mode. |
401 Future<T> typedFuture = value; | 452 Future<T> typedFuture = value; |
402 if (typedFuture is _Future) { | 453 if (typedFuture is _Future) { |
403 _Future<T> coreFuture = typedFuture; | 454 _Future<T> coreFuture = typedFuture; |
404 if (coreFuture._hasError) { | 455 if (coreFuture._hasError) { |
405 // Case 1 from above. Delay completion to enable the user to register | 456 // Case 1 from above. Delay completion to enable the user to register |
406 // callbacks. | 457 // callbacks. |
407 _markPendingCompletion(); | 458 _setPendingComplete(); |
408 _zone.scheduleMicrotask(() { | 459 _zone.scheduleMicrotask(() { |
409 _chainCoreFuture(coreFuture, this); | 460 _chainCoreFuture(coreFuture, this); |
410 }); | 461 }); |
411 } else { | 462 } else { |
412 _chainCoreFuture(coreFuture, this); | 463 _chainCoreFuture(coreFuture, this); |
413 } | 464 } |
414 } else { | 465 } else { |
415 // Case 2 from above. Chain the future immidiately. | 466 // Case 2 from above. Chain the future immidiately. |
416 // Note that we are still completing asynchronously (through | 467 // Note that we are still completing asynchronously (through |
417 // _chainForeignFuture). | 468 // _chainForeignFuture). |
418 _chainForeignFuture(typedFuture, this); | 469 _chainForeignFuture(typedFuture, this); |
419 } | 470 } |
420 return; | 471 return; |
421 } else { | 472 } else { |
422 T typedValue = value; | 473 T typedValue = value; |
| 474 assert(typedValue is T); // Avoid warning that typedValue is unused. |
423 } | 475 } |
424 | 476 |
425 _markPendingCompletion(); | 477 _setPendingComplete(); |
426 _zone.scheduleMicrotask(() { | 478 _zone.scheduleMicrotask(() { |
427 _completeWithValue(value); | 479 _completeWithValue(value); |
428 }); | 480 }); |
429 } | 481 } |
430 | 482 |
431 void _asyncCompleteError(error, StackTrace stackTrace) { | 483 void _asyncCompleteError(error, StackTrace stackTrace) { |
432 assert(!_isComplete); | 484 assert(!_isComplete); |
433 | 485 |
434 _markPendingCompletion(); | 486 _setPendingComplete(); |
435 _zone.scheduleMicrotask(() { | 487 _zone.scheduleMicrotask(() { |
436 _completeError(error, stackTrace); | 488 _completeError(error, stackTrace); |
437 }); | 489 }); |
438 } | 490 } |
439 | 491 |
440 /** | 492 /** |
441 * Propagates the value/error of [source] to its [listeners], executing the | 493 * Propagates the value/error of [source] to its [listeners], executing the |
442 * listeners' callbacks. | 494 * listeners' callbacks. |
443 */ | 495 */ |
444 static void _propagateToListeners(_Future source, _FutureListener listeners) { | 496 static void _propagateToListeners(_Future source, _FutureListener listeners) { |
(...skipping 11 matching lines...) Expand all Loading... |
456 // Usually futures only have one listener. If they have several, we | 508 // Usually futures only have one listener. If they have several, we |
457 // call handle them separately in recursive calls, continuing | 509 // call handle them separately in recursive calls, continuing |
458 // here only when there is only one listener left. | 510 // here only when there is only one listener left. |
459 while (listeners._nextListener != null) { | 511 while (listeners._nextListener != null) { |
460 _FutureListener listener = listeners; | 512 _FutureListener listener = listeners; |
461 listeners = listener._nextListener; | 513 listeners = listener._nextListener; |
462 listener._nextListener = null; | 514 listener._nextListener = null; |
463 _propagateToListeners(source, listener); | 515 _propagateToListeners(source, listener); |
464 } | 516 } |
465 _FutureListener listener = listeners; | 517 _FutureListener listener = listeners; |
| 518 final sourceResult = source._resultOrListeners; |
466 // Do the actual propagation. | 519 // Do the actual propagation. |
467 // Set initial state of listenerHasError and listenerValueOrError. These | 520 // Set initial state of listenerHasError and listenerValueOrError. These |
468 // variables are updated with the outcome of potential callbacks. | 521 // variables are updated with the outcome of potential callbacks. |
| 522 // Non-error results, including futures, are stored in |
| 523 // listenerValueOrError and listenerHasError is set to false. Errors |
| 524 // are stored in listenerValueOrError as an [AsyncError] and |
| 525 // listenerHasError is set to true. |
469 bool listenerHasError = hasError; | 526 bool listenerHasError = hasError; |
470 final sourceResult = source._resultOrListeners; | |
471 var listenerValueOrError = sourceResult; | 527 var listenerValueOrError = sourceResult; |
472 | 528 |
473 // Only if we either have an error or callbacks, go into this, somewhat | 529 // Only if we either have an error or callbacks, go into this, somewhat |
474 // expensive, branch. Here we'll enter/leave the zone. Many futures | 530 // expensive, branch. Here we'll enter/leave the zone. Many futures |
475 // don't have callbacks, so this is a significant optimization. | 531 // don't have callbacks, so this is a significant optimization. |
476 if (hasError || listener.handlesValue || listener.handlesComplete) { | 532 if (hasError || listener.handlesValue || listener.handlesComplete) { |
477 Zone zone = listener._zone; | 533 Zone zone = listener._zone; |
478 if (hasError && !source._zone.inSameErrorZone(zone)) { | 534 if (hasError && !source._zone.inSameErrorZone(zone)) { |
479 // Don't cross zone boundaries with errors. | 535 // Don't cross zone boundaries with errors. |
480 AsyncError asyncError = source._error; | 536 AsyncError asyncError = source._error; |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 | 647 |
592 // If the listener's value is a future we need to chain it. Note that | 648 // If the listener's value is a future we need to chain it. Note that |
593 // this can only happen if there is a callback. | 649 // this can only happen if there is a callback. |
594 if (listenerValueOrError is Future) { | 650 if (listenerValueOrError is Future) { |
595 Future chainSource = listenerValueOrError; | 651 Future chainSource = listenerValueOrError; |
596 // Shortcut if the chain-source is already completed. Just continue | 652 // Shortcut if the chain-source is already completed. Just continue |
597 // the loop. | 653 // the loop. |
598 _Future result = listener.result; | 654 _Future result = listener.result; |
599 if (chainSource is _Future) { | 655 if (chainSource is _Future) { |
600 if (chainSource._isComplete) { | 656 if (chainSource._isComplete) { |
601 // propagate the value (simulating a tail call). | 657 listeners = result._removeListeners(); |
602 result._setChained(); | 658 result._cloneResult(chainSource); |
603 source = chainSource; | 659 source = chainSource; |
604 listeners = new _FutureListener.chain(result); | |
605 continue; | 660 continue; |
606 } else { | 661 } else { |
607 _chainCoreFuture(chainSource, result); | 662 _chainCoreFuture(chainSource, result); |
608 } | 663 } |
609 } else { | 664 } else { |
610 _chainForeignFuture(chainSource, result); | 665 _chainForeignFuture(chainSource, result); |
611 } | 666 } |
612 return; | 667 return; |
613 } | 668 } |
614 } | 669 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
652 } | 707 } |
653 }, onError: (e, s) { | 708 }, onError: (e, s) { |
654 if (timer.isActive) { | 709 if (timer.isActive) { |
655 timer.cancel(); | 710 timer.cancel(); |
656 result._completeError(e, s); | 711 result._completeError(e, s); |
657 } | 712 } |
658 }); | 713 }); |
659 return result; | 714 return result; |
660 } | 715 } |
661 } | 716 } |
OLD | NEW |