| 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 typedef dynamic _FutureOnError(error); | 9 typedef dynamic _FutureOnError(error); |
| 10 /** Test used by [Future.catchError] to handle skip some errors. */ | 10 /** Test used by [Future.catchError] to handle skip some errors. */ |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 // just use the PENDING_COMPLETE state instead. | 69 // just use the PENDING_COMPLETE state instead. |
| 70 static const int _CHAINED = 2; | 70 static const int _CHAINED = 2; |
| 71 /// The future has been completed with a value result. | 71 /// The future has been completed with a value result. |
| 72 static const int _VALUE = 4; | 72 static const int _VALUE = 4; |
| 73 /// The future has been completed with an error result. | 73 /// The future has been completed with an error result. |
| 74 static const int _ERROR = 8; | 74 static const int _ERROR = 8; |
| 75 | 75 |
| 76 /** Whether the future is complete, and as what. */ | 76 /** Whether the future is complete, and as what. */ |
| 77 int _state = _INCOMPLETE; | 77 int _state = _INCOMPLETE; |
| 78 | 78 |
| 79 final _Zone _zone = _Zone.current.fork(); | 79 final Zone _zone = Zone.current; |
| 80 | 80 |
| 81 bool get _mayComplete => _state == _INCOMPLETE; | 81 bool get _mayComplete => _state == _INCOMPLETE; |
| 82 bool get _isChained => _state == _CHAINED; | 82 bool get _isChained => _state == _CHAINED; |
| 83 bool get _isComplete => _state >= _VALUE; | 83 bool get _isComplete => _state >= _VALUE; |
| 84 bool get _hasValue => _state == _VALUE; | 84 bool get _hasValue => _state == _VALUE; |
| 85 bool get _hasError => _state == _ERROR; | 85 bool get _hasError => _state == _ERROR; |
| 86 | 86 |
| 87 set _isChained(bool value) { | 87 set _isChained(bool value) { |
| 88 if (value) { | 88 if (value) { |
| 89 assert(!_isComplete); | 89 assert(!_isComplete); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 _onErrorCallback = null, _whenCompleteActionCallback = null { | 148 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 149 _asyncComplete(value); | 149 _asyncComplete(value); |
| 150 } | 150 } |
| 151 | 151 |
| 152 _Future.immediateError(var error, [Object stackTrace]) | 152 _Future.immediateError(var error, [Object stackTrace]) |
| 153 : _onValueCallback = null, _errorTestCallback = null, | 153 : _onValueCallback = null, _errorTestCallback = null, |
| 154 _onErrorCallback = null, _whenCompleteActionCallback = null { | 154 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 155 _asyncCompleteError(error, stackTrace); | 155 _asyncCompleteError(error, stackTrace); |
| 156 } | 156 } |
| 157 | 157 |
| 158 _Future._then(this._onValueCallback, this._onErrorCallback) | 158 _Future._then(onValueCallback(value), onErrorCallback(e)) |
| 159 : _errorTestCallback = null, _whenCompleteActionCallback = null { | 159 : _onValueCallback = Zone._current.registerCallback1(onValueCallback), |
| 160 _zone.expectCallback(); | 160 _onErrorCallback = Zone._current.registerCallback1(onErrorCallback), |
| 161 } | 161 _errorTestCallback = null, |
| 162 _whenCompleteActionCallback = null; |
| 162 | 163 |
| 163 _Future._catchError(this._onErrorCallback, this._errorTestCallback) | 164 _Future._catchError(onErrorCallback(e), bool errorTestCallback(e)) |
| 164 : _onValueCallback = null, _whenCompleteActionCallback = null { | 165 : _onErrorCallback = Zone._current.registerCallback1(onErrorCallback), |
| 165 _zone.expectCallback(); | 166 _errorTestCallback = Zone._current.registerCallback1(errorTestCallback), |
| 166 } | 167 _onValueCallback = null, |
| 168 _whenCompleteActionCallback = null; |
| 167 | 169 |
| 168 _Future._whenComplete(this._whenCompleteActionCallback) | 170 _Future._whenComplete(whenCompleteActionCallback()) |
| 169 : _onValueCallback = null, _errorTestCallback = null, | 171 : _whenCompleteActionCallback = |
| 170 _onErrorCallback = null { | 172 Zone._current.registerCallback(whenCompleteActionCallback), |
| 171 _zone.expectCallback(); | 173 _onValueCallback = null, |
| 172 } | 174 _errorTestCallback = null, |
| 175 _onErrorCallback = null; |
| 173 | 176 |
| 174 Future then(f(T value), { onError(error) }) { | 177 Future then(f(T value), { onError(error) }) { |
| 175 _Future result; | 178 _Future result; |
| 176 result = new _Future._then(f, onError); | 179 result = new _Future._then(f, onError); |
| 177 _addListener(result); | 180 _addListener(result); |
| 178 return result; | 181 return result; |
| 179 } | 182 } |
| 180 | 183 |
| 181 Future catchError(f(error), { bool test(error) }) { | 184 Future catchError(f(error), { bool test(error) }) { |
| 182 _Future result = new _Future._catchError(f, test); | 185 _Future result = new _Future._catchError(f, test); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 void _setError(Object error) { | 219 void _setError(Object error) { |
| 217 assert(!_isComplete); // But may have a completion pending. | 220 assert(!_isComplete); // But may have a completion pending. |
| 218 _state = _ERROR; | 221 _state = _ERROR; |
| 219 _resultOrListeners = error; | 222 _resultOrListeners = error; |
| 220 } | 223 } |
| 221 | 224 |
| 222 void _addListener(_Future listener) { | 225 void _addListener(_Future listener) { |
| 223 assert(listener._nextListener == null); | 226 assert(listener._nextListener == null); |
| 224 if (_isComplete) { | 227 if (_isComplete) { |
| 225 // Handle late listeners asynchronously. | 228 // Handle late listeners asynchronously. |
| 226 runAsync(() { | 229 _zone.runAsync(() { |
| 227 _propagateToListeners(this, listener); | 230 _propagateToListeners(this, listener); |
| 228 }); | 231 }); |
| 229 } else { | 232 } else { |
| 230 listener._nextListener = _resultOrListeners; | 233 listener._nextListener = _resultOrListeners; |
| 231 _resultOrListeners = listener; | 234 _resultOrListeners = listener; |
| 232 } | 235 } |
| 233 } | 236 } |
| 234 | 237 |
| 235 _Future _removeListeners() { | 238 _Future _removeListeners() { |
| 236 // Reverse listeners before returning them, so the resulting list is in | 239 // Reverse listeners before returning them, so the resulting list is in |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 (value is! _Future || !(value as _Future)._isComplete)) { | 328 (value is! _Future || !(value as _Future)._isComplete)) { |
| 326 // Case 2 from above. We need to register. | 329 // Case 2 from above. We need to register. |
| 327 // Note that we are still completing asynchronously: either we register | 330 // Note that we are still completing asynchronously: either we register |
| 328 // through .then (in which case the completing is asynchronous), or we | 331 // through .then (in which case the completing is asynchronous), or we |
| 329 // have a _Future which isn't complete yet. | 332 // have a _Future which isn't complete yet. |
| 330 _complete(value); | 333 _complete(value); |
| 331 return; | 334 return; |
| 332 } | 335 } |
| 333 | 336 |
| 334 _markPendingCompletion(); | 337 _markPendingCompletion(); |
| 335 runAsync(() { | 338 _zone.runAsync(() { |
| 336 _complete(value); | 339 _complete(value); |
| 337 }); | 340 }); |
| 338 } | 341 } |
| 339 | 342 |
| 340 void _asyncCompleteError(error, [StackTrace stackTrace]) { | 343 void _asyncCompleteError(error, [StackTrace stackTrace]) { |
| 341 assert(!_isComplete); | 344 assert(!_isComplete); |
| 342 assert(_onValue == null); | 345 assert(_onValue == null); |
| 343 assert(_onError == null); | 346 assert(_onError == null); |
| 344 assert(_whenCompleteAction == null); | 347 assert(_whenCompleteAction == null); |
| 345 assert(_errorTest == null); | 348 assert(_errorTest == null); |
| 346 | 349 |
| 347 _markPendingCompletion(); | 350 _markPendingCompletion(); |
| 348 runAsync(() { | 351 _zone.runAsync(() { |
| 349 _completeError(error, stackTrace); | 352 _completeError(error, stackTrace); |
| 350 }); | 353 }); |
| 351 } | 354 } |
| 352 | 355 |
| 353 /** | 356 /** |
| 354 * Propagates the value/error of [source] to its [listeners]. | 357 * Propagates the value/error of [source] to its [listeners]. |
| 355 * | 358 * |
| 356 * Unlinks all listeners and propagates the source to each listener | 359 * Unlinks all listeners and propagates the source to each listener |
| 357 * separately. | 360 * separately. |
| 358 */ | 361 */ |
| (...skipping 30 matching lines...) Expand all Loading... |
| 389 // Usually futures only have one listener. If they have several, we | 392 // Usually futures only have one listener. If they have several, we |
| 390 // handle them specially. | 393 // handle them specially. |
| 391 _propagateMultipleListeners(source, listeners); | 394 _propagateMultipleListeners(source, listeners); |
| 392 return; | 395 return; |
| 393 } | 396 } |
| 394 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { | 397 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { |
| 395 // Don't cross zone boundaries with errors. | 398 // Don't cross zone boundaries with errors. |
| 396 source._zone.handleUncaughtError(source._error); | 399 source._zone.handleUncaughtError(source._error); |
| 397 return; | 400 return; |
| 398 } | 401 } |
| 399 if (!identical(_Zone.current, listener._zone)) { | 402 if (!identical(Zone.current, listener._zone)) { |
| 400 // Run the propagation in the listener's zone to avoid | 403 // Run the propagation in the listener's zone to avoid |
| 401 // zone transitions. The idea is that many chained futures will | 404 // zone transitions. The idea is that many chained futures will |
| 402 // be in the same zone. | 405 // be in the same zone. |
| 403 listener._zone.executePeriodicCallback(() { | 406 listener._zone.run(() { |
| 404 _propagateToListeners(source, listener); | 407 _propagateToListeners(source, listener); |
| 405 }); | 408 }); |
| 406 return; | 409 return; |
| 407 } | 410 } |
| 408 | 411 |
| 409 // Do the actual propagation. | 412 // Do the actual propagation. |
| 410 // TODO(floitsch): Do we need to go through the zone even if we | 413 // TODO(floitsch): Do we need to go through the zone even if we |
| 411 // don't have a callback to execute? | 414 // don't have a callback to execute? |
| 412 bool listenerHasValue; | 415 bool listenerHasValue; |
| 413 var listenerValueOrError; | 416 var listenerValueOrError; |
| 414 // Set to true if a whenComplete needs to wait for a future. | 417 // Set to true if a whenComplete needs to wait for a future. |
| 415 // The whenComplete action will resume the propagation by itself. | 418 // The whenComplete action will resume the propagation by itself. |
| 416 bool isPropagationAborted = false; | 419 bool isPropagationAborted = false; |
| 417 // Even though we are already in the right zone (due to the optimization | 420 // Even though we are already in the right zone (due to the optimization |
| 418 // above), we still need to go through the zone. The overhead of | 421 // above), we still need to go through the zone. The overhead of |
| 419 // executeCallback is however smaller when it is already in the correct | 422 // executeCallback is however smaller when it is already in the correct |
| 420 // zone. | 423 // zone. |
| 421 // TODO(floitsch): only run callbacks in the zone, not the whole | 424 // TODO(floitsch): only run callbacks in the zone, not the whole |
| 422 // handling code. | 425 // handling code. |
| 423 listener._zone.executeCallback(() { | 426 listener._zone.run(() { |
| 424 // TODO(floitsch): mark the listener as pending completion. Currently | 427 // TODO(floitsch): mark the listener as pending completion. Currently |
| 425 // we can't do this, since the markPendingCompletion verifies that | 428 // we can't do this, since the markPendingCompletion verifies that |
| 426 // the future is not already marked (or chained). | 429 // the future is not already marked (or chained). |
| 427 try { | 430 try { |
| 428 if (!hasError) { | 431 if (!hasError) { |
| 429 var value = source._value; | 432 var value = source._value; |
| 430 if (listener._onValue != null) { | 433 if (listener._onValue != null) { |
| 431 listenerValueOrError = listener._onValue(value); | 434 listenerValueOrError = listener._onValue(value); |
| 432 listenerHasValue = true; | 435 listenerHasValue = true; |
| 433 } else { | 436 } else { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 463 // When there is an error, we have to make the error the new | 466 // When there is an error, we have to make the error the new |
| 464 // result of the current listener. | 467 // result of the current listener. |
| 465 if (completeResult is! _Future) { | 468 if (completeResult is! _Future) { |
| 466 // This should be a rare case. | 469 // This should be a rare case. |
| 467 completeResult = new _Future(); | 470 completeResult = new _Future(); |
| 468 completeResult._setError(error); | 471 completeResult._setError(error); |
| 469 } | 472 } |
| 470 _propagateToListeners(completeResult, listener); | 473 _propagateToListeners(completeResult, listener); |
| 471 }); | 474 }); |
| 472 isPropagationAborted = true; | 475 isPropagationAborted = true; |
| 473 // We will reenter the listener's zone. | |
| 474 listener._zone.expectCallback(); | |
| 475 } | 476 } |
| 476 } | 477 } |
| 477 } catch (e, s) { | 478 } catch (e, s) { |
| 478 // Set the exception as error. | 479 // Set the exception as error. |
| 479 listenerValueOrError = _asyncError(e, s); | 480 listenerValueOrError = _asyncError(e, s); |
| 480 listenerHasValue = false; | 481 listenerHasValue = false; |
| 481 } | 482 } |
| 482 if (listenerHasValue && listenerValueOrError is Future) { | |
| 483 // We are going to reenter the zone to finish what we started. | |
| 484 listener._zone.expectCallback(); | |
| 485 } | |
| 486 }); | 483 }); |
| 487 if (isPropagationAborted) return; | 484 if (isPropagationAborted) return; |
| 488 // If the listener's value is a future we need to chain it. | 485 // If the listener's value is a future we need to chain it. |
| 489 if (listenerHasValue && listenerValueOrError is Future) { | 486 if (listenerHasValue && listenerValueOrError is Future) { |
| 490 Future chainSource = listenerValueOrError; | 487 Future chainSource = listenerValueOrError; |
| 491 // Shortcut if the chain-source is already completed. Just continue the | 488 // Shortcut if the chain-source is already completed. Just continue the |
| 492 // loop. | 489 // loop. |
| 493 if (chainSource is _Future && (chainSource as _Future)._isComplete) { | 490 if (chainSource is _Future && (chainSource as _Future)._isComplete) { |
| 494 // propagate the value (simulating a tail call). | 491 // propagate the value (simulating a tail call). |
| 495 listener._isChained = true; | 492 listener._isChained = true; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 506 listener._setValue(listenerValueOrError); | 503 listener._setValue(listenerValueOrError); |
| 507 } else { | 504 } else { |
| 508 listeners = listener._removeListeners(); | 505 listeners = listener._removeListeners(); |
| 509 listener._setError(listenerValueOrError); | 506 listener._setError(listenerValueOrError); |
| 510 } | 507 } |
| 511 // Prepare for next round. | 508 // Prepare for next round. |
| 512 source = listener; | 509 source = listener; |
| 513 } | 510 } |
| 514 } | 511 } |
| 515 } | 512 } |
| OLD | NEW |