| 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; |
| 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 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 final _FutureOnError _onErrorCallback; | 133 final _FutureOnError _onErrorCallback; |
| 134 final _FutureAction _whenCompleteActionCallback; | 134 final _FutureAction _whenCompleteActionCallback; |
| 135 | 135 |
| 136 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; | 136 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; |
| 137 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; | 137 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; |
| 138 _FutureOnError get _onError => _isChained ? null : _onErrorCallback; | 138 _FutureOnError get _onError => _isChained ? null : _onErrorCallback; |
| 139 _FutureAction get _whenCompleteAction | 139 _FutureAction get _whenCompleteAction |
| 140 => _isChained ? null : _whenCompleteActionCallback; | 140 => _isChained ? null : _whenCompleteActionCallback; |
| 141 | 141 |
| 142 _Future() | 142 _Future() |
| 143 : _onValueCallback = null, _errorTestCallback = null, | 143 : _zone = Zone.current, |
| 144 _onValueCallback = null, _errorTestCallback = null, |
| 144 _onErrorCallback = null, _whenCompleteActionCallback = null; | 145 _onErrorCallback = null, _whenCompleteActionCallback = null; |
| 145 | 146 |
| 146 /// Valid types for value: `T` or `Future<T>`. | 147 /// Valid types for value: `T` or `Future<T>`. |
| 147 _Future.immediate(value) | 148 _Future.immediate(value) |
| 148 : _onValueCallback = null, _errorTestCallback = null, | 149 : _zone = Zone.current, |
| 150 _onValueCallback = null, _errorTestCallback = null, |
| 149 _onErrorCallback = null, _whenCompleteActionCallback = null { | 151 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 150 _asyncComplete(value); | 152 _asyncComplete(value); |
| 151 } | 153 } |
| 152 | 154 |
| 153 _Future.immediateError(var error, [Object stackTrace]) | 155 _Future.immediateError(var error, [Object stackTrace]) |
| 154 : _onValueCallback = null, _errorTestCallback = null, | 156 : _zone = Zone.current, |
| 157 _onValueCallback = null, _errorTestCallback = null, |
| 155 _onErrorCallback = null, _whenCompleteActionCallback = null { | 158 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 156 _asyncCompleteError(error, stackTrace); | 159 _asyncCompleteError(error, stackTrace); |
| 157 } | 160 } |
| 158 | 161 |
| 159 _Future._then(this._onValueCallback, this._onErrorCallback) | 162 _Future._then(onValueCallback(value), onErrorCallback(e)) |
| 160 : _errorTestCallback = null, _whenCompleteActionCallback = null { | 163 : _zone = Zone.current, |
| 161 _zone.expectCallback(); | 164 _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback), |
| 162 } | 165 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), |
| 166 _errorTestCallback = null, |
| 167 _whenCompleteActionCallback = null; |
| 163 | 168 |
| 164 _Future._catchError(this._onErrorCallback, this._errorTestCallback) | 169 _Future._catchError(onErrorCallback(e), bool errorTestCallback(e)) |
| 165 : _onValueCallback = null, _whenCompleteActionCallback = null { | 170 : _zone = Zone.current, |
| 166 _zone.expectCallback(); | 171 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), |
| 167 } | 172 _errorTestCallback = Zone.current.registerUnaryCallback(errorTestCallback)
, |
| 173 _onValueCallback = null, |
| 174 _whenCompleteActionCallback = null; |
| 168 | 175 |
| 169 _Future._whenComplete(this._whenCompleteActionCallback) | 176 _Future._whenComplete(whenCompleteActionCallback()) |
| 170 : _onValueCallback = null, _errorTestCallback = null, | 177 : _zone = Zone.current, |
| 171 _onErrorCallback = null { | 178 _whenCompleteActionCallback = |
| 172 _zone.expectCallback(); | 179 Zone.current.registerCallback(whenCompleteActionCallback), |
| 173 } | 180 _onValueCallback = null, |
| 181 _errorTestCallback = null, |
| 182 _onErrorCallback = null; |
| 174 | 183 |
| 175 Future then(f(T value), { onError(error) }) { | 184 Future then(f(T value), { onError(error) }) { |
| 176 _Future result; | 185 _Future result; |
| 177 result = new _Future._then(f, onError); | 186 result = new _Future._then(f, onError); |
| 178 _addListener(result); | 187 _addListener(result); |
| 179 return result; | 188 return result; |
| 180 } | 189 } |
| 181 | 190 |
| 182 Future catchError(f(error), { bool test(error) }) { | 191 Future catchError(f(error), { bool test(error) }) { |
| 183 _Future result = new _Future._catchError(f, test); | 192 _Future result = new _Future._catchError(f, test); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 void _setError(Object error) { | 226 void _setError(Object error) { |
| 218 assert(!_isComplete); // But may have a completion pending. | 227 assert(!_isComplete); // But may have a completion pending. |
| 219 _state = _ERROR; | 228 _state = _ERROR; |
| 220 _resultOrListeners = error; | 229 _resultOrListeners = error; |
| 221 } | 230 } |
| 222 | 231 |
| 223 void _addListener(_Future listener) { | 232 void _addListener(_Future listener) { |
| 224 assert(listener._nextListener == null); | 233 assert(listener._nextListener == null); |
| 225 if (_isComplete) { | 234 if (_isComplete) { |
| 226 // Handle late listeners asynchronously. | 235 // Handle late listeners asynchronously. |
| 227 _zone.runAsync(() { | 236 _zone.scheduleMicrotask(() { |
| 228 _propagateToListeners(this, listener); | 237 _propagateToListeners(this, listener); |
| 229 }, _zone); | 238 }); |
| 230 } else { | 239 } else { |
| 231 listener._nextListener = _resultOrListeners; | 240 listener._nextListener = _resultOrListeners; |
| 232 _resultOrListeners = listener; | 241 _resultOrListeners = listener; |
| 233 } | 242 } |
| 234 } | 243 } |
| 235 | 244 |
| 236 _Future _removeListeners() { | 245 _Future _removeListeners() { |
| 237 // Reverse listeners before returning them, so the resulting list is in | 246 // Reverse listeners before returning them, so the resulting list is in |
| 238 // subscription order. | 247 // subscription order. |
| 239 assert(!_isComplete); | 248 assert(!_isComplete); |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 (value is! _Future || !(value as _Future)._isComplete)) { | 343 (value is! _Future || !(value as _Future)._isComplete)) { |
| 335 // Case 2 from above. We need to register. | 344 // Case 2 from above. We need to register. |
| 336 // Note that we are still completing asynchronously: either we register | 345 // Note that we are still completing asynchronously: either we register |
| 337 // through .then (in which case the completing is asynchronous), or we | 346 // through .then (in which case the completing is asynchronous), or we |
| 338 // have a _Future which isn't complete yet. | 347 // have a _Future which isn't complete yet. |
| 339 _complete(value); | 348 _complete(value); |
| 340 return; | 349 return; |
| 341 } | 350 } |
| 342 | 351 |
| 343 _markPendingCompletion(); | 352 _markPendingCompletion(); |
| 344 _zone.runAsync(() { _complete(value); }, _zone); | 353 _zone.scheduleMicrotask(() { |
| 354 _complete(value); |
| 355 }); |
| 345 } | 356 } |
| 346 | 357 |
| 347 void _asyncCompleteError(error, [StackTrace stackTrace]) { | 358 void _asyncCompleteError(error, [StackTrace stackTrace]) { |
| 348 assert(!_isComplete); | 359 assert(!_isComplete); |
| 349 assert(_onValue == null); | 360 assert(_onValue == null); |
| 350 assert(_onError == null); | 361 assert(_onError == null); |
| 351 assert(_whenCompleteAction == null); | 362 assert(_whenCompleteAction == null); |
| 352 assert(_errorTest == null); | 363 assert(_errorTest == null); |
| 353 | 364 |
| 354 _markPendingCompletion(); | 365 _markPendingCompletion(); |
| 355 _zone.runAsync(() { _completeError(error, stackTrace); }, _zone); | 366 _zone.scheduleMicrotask(() { |
| 367 _completeError(error, stackTrace); |
| 368 }); |
| 356 } | 369 } |
| 357 | 370 |
| 358 /** | 371 /** |
| 359 * Propagates the value/error of [source] to its [listeners]. | 372 * Propagates the value/error of [source] to its [listeners]. |
| 360 * | 373 * |
| 361 * Unlinks all listeners and propagates the source to each listener | 374 * Unlinks all listeners and propagates the source to each listener |
| 362 * separately. | 375 * separately. |
| 363 */ | 376 */ |
| 364 static void _propagateMultipleListeners(_Future source, _Future listeners) { | 377 static void _propagateMultipleListeners(_Future source, _Future listeners) { |
| 365 assert(listeners != null); | 378 assert(listeners != null); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 394 // Usually futures only have one listener. If they have several, we | 407 // Usually futures only have one listener. If they have several, we |
| 395 // handle them specially. | 408 // handle them specially. |
| 396 _propagateMultipleListeners(source, listeners); | 409 _propagateMultipleListeners(source, listeners); |
| 397 return; | 410 return; |
| 398 } | 411 } |
| 399 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { | 412 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { |
| 400 // Don't cross zone boundaries with errors. | 413 // Don't cross zone boundaries with errors. |
| 401 source._zone.handleUncaughtError(source._error); | 414 source._zone.handleUncaughtError(source._error); |
| 402 return; | 415 return; |
| 403 } | 416 } |
| 404 if (!identical(_Zone.current, listener._zone)) { | 417 if (!identical(Zone.current, listener._zone)) { |
| 405 // Run the propagation in the listener's zone to avoid | 418 // Run the propagation in the listener's zone to avoid |
| 406 // zone transitions. The idea is that many chained futures will | 419 // zone transitions. The idea is that many chained futures will |
| 407 // be in the same zone. | 420 // be in the same zone. |
| 408 listener._zone.executePeriodicCallback(() { | 421 listener._zone.run(() { |
| 409 _propagateToListeners(source, listener); | 422 _propagateToListeners(source, listener); |
| 410 }); | 423 }); |
| 411 return; | 424 return; |
| 412 } | 425 } |
| 413 | 426 |
| 414 // Do the actual propagation. | 427 // Do the actual propagation. |
| 415 // TODO(floitsch): Do we need to go through the zone even if we | 428 // TODO(floitsch): Do we need to go through the zone even if we |
| 416 // don't have a callback to execute? | 429 // don't have a callback to execute? |
| 417 bool listenerHasValue; | 430 bool listenerHasValue; |
| 418 var listenerValueOrError; | 431 var listenerValueOrError; |
| 419 // Set to true if a whenComplete needs to wait for a future. | 432 // Set to true if a whenComplete needs to wait for a future. |
| 420 // The whenComplete action will resume the propagation by itself. | 433 // The whenComplete action will resume the propagation by itself. |
| 421 bool isPropagationAborted = false; | 434 bool isPropagationAborted = false; |
| 422 // Even though we are already in the right zone (due to the optimization | 435 // Even though we are already in the right zone (due to the optimization |
| 423 // above), we still need to go through the zone. The overhead of | 436 // above), we still need to go through the zone. The overhead of |
| 424 // executeCallback is however smaller when it is already in the correct | 437 // executeCallback is however smaller when it is already in the correct |
| 425 // zone. | 438 // zone. |
| 426 // TODO(floitsch): only run callbacks in the zone, not the whole | 439 // TODO(floitsch): only run callbacks in the zone, not the whole |
| 427 // handling code. | 440 // handling code. |
| 428 listener._zone.executeCallback(() { | 441 listener._zone.run(() { |
| 429 // TODO(floitsch): mark the listener as pending completion. Currently | 442 // TODO(floitsch): mark the listener as pending completion. Currently |
| 430 // we can't do this, since the markPendingCompletion verifies that | 443 // we can't do this, since the markPendingCompletion verifies that |
| 431 // the future is not already marked (or chained). | 444 // the future is not already marked (or chained). |
| 432 try { | 445 try { |
| 433 if (!hasError) { | 446 if (!hasError) { |
| 434 var value = source._value; | 447 var value = source._value; |
| 435 if (listener._onValue != null) { | 448 if (listener._onValue != null) { |
| 436 listenerValueOrError = listener._onValue(value); | 449 listenerValueOrError = listener._onValue(value); |
| 437 listenerHasValue = true; | 450 listenerHasValue = true; |
| 438 } else { | 451 } else { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 468 // When there is an error, we have to make the error the new | 481 // When there is an error, we have to make the error the new |
| 469 // result of the current listener. | 482 // result of the current listener. |
| 470 if (completeResult is! _Future) { | 483 if (completeResult is! _Future) { |
| 471 // This should be a rare case. | 484 // This should be a rare case. |
| 472 completeResult = new _Future(); | 485 completeResult = new _Future(); |
| 473 completeResult._setError(error); | 486 completeResult._setError(error); |
| 474 } | 487 } |
| 475 _propagateToListeners(completeResult, listener); | 488 _propagateToListeners(completeResult, listener); |
| 476 }); | 489 }); |
| 477 isPropagationAborted = true; | 490 isPropagationAborted = true; |
| 478 // We will reenter the listener's zone. | |
| 479 listener._zone.expectCallback(); | |
| 480 } | 491 } |
| 481 } | 492 } |
| 482 } catch (e, s) { | 493 } catch (e, s) { |
| 483 // Set the exception as error. | 494 // Set the exception as error. |
| 484 listenerValueOrError = _asyncError(e, s); | 495 listenerValueOrError = _asyncError(e, s); |
| 485 listenerHasValue = false; | 496 listenerHasValue = false; |
| 486 } | 497 } |
| 487 if (listenerHasValue && listenerValueOrError is Future) { | |
| 488 // We are going to reenter the zone to finish what we started. | |
| 489 listener._zone.expectCallback(); | |
| 490 } | |
| 491 }); | 498 }); |
| 492 if (isPropagationAborted) return; | 499 if (isPropagationAborted) return; |
| 493 // If the listener's value is a future we need to chain it. | 500 // If the listener's value is a future we need to chain it. |
| 494 if (listenerHasValue && listenerValueOrError is Future) { | 501 if (listenerHasValue && listenerValueOrError is Future) { |
| 495 Future chainSource = listenerValueOrError; | 502 Future chainSource = listenerValueOrError; |
| 496 // Shortcut if the chain-source is already completed. Just continue the | 503 // Shortcut if the chain-source is already completed. Just continue the |
| 497 // loop. | 504 // loop. |
| 498 if (chainSource is _Future && (chainSource as _Future)._isComplete) { | 505 if (chainSource is _Future && (chainSource as _Future)._isComplete) { |
| 499 // propagate the value (simulating a tail call). | 506 // propagate the value (simulating a tail call). |
| 500 listener._isChained = true; | 507 listener._isChained = true; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 511 listener._setValue(listenerValueOrError); | 518 listener._setValue(listenerValueOrError); |
| 512 } else { | 519 } else { |
| 513 listeners = listener._removeListeners(); | 520 listeners = listener._removeListeners(); |
| 514 listener._setError(listenerValueOrError); | 521 listener._setError(listenerValueOrError); |
| 515 } | 522 } |
| 516 // Prepare for next round. | 523 // Prepare for next round. |
| 517 source = listener; | 524 source = listener; |
| 518 } | 525 } |
| 519 } | 526 } |
| 520 } | 527 } |
| OLD | NEW |