| 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); | |
| 10 /** Test used by [Future.catchError] to handle skip some errors. */ | 9 /** Test used by [Future.catchError] to handle skip some errors. */ |
| 11 typedef bool _FutureErrorTest(var error); | 10 typedef bool _FutureErrorTest(var error); |
| 12 /** Used by [WhenFuture]. */ | 11 /** Used by [WhenFuture]. */ |
| 13 typedef _FutureAction(); | 12 typedef _FutureAction(); |
| 14 | 13 |
| 15 abstract class _Completer<T> implements Completer<T> { | 14 abstract class _Completer<T> implements Completer<T> { |
| 16 final _Future<T> future = new _Future<T>(); | 15 final _Future<T> future = new _Future<T>(); |
| 17 | 16 |
| 18 void complete([T value]); | 17 void complete([T value]); |
| 19 | 18 |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 // TODO(floitsch): since single listeners are the common case we should | 122 // TODO(floitsch): since single listeners are the common case we should |
| 124 // use a bit to indicate that the _resultOrListeners contains a container. | 123 // use a bit to indicate that the _resultOrListeners contains a container. |
| 125 _Future _nextListener; | 124 _Future _nextListener; |
| 126 | 125 |
| 127 // TODO(floitsch): we only need two closure fields to store the callbacks. | 126 // TODO(floitsch): we only need two closure fields to store the callbacks. |
| 128 // If we store the type of a closure in the state field (where there are | 127 // If we store the type of a closure in the state field (where there are |
| 129 // still bits left), we can just store two closures instead of using 4 | 128 // still bits left), we can just store two closures instead of using 4 |
| 130 // fields of which 2 are always null. | 129 // fields of which 2 are always null. |
| 131 final _FutureOnValue _onValueCallback; | 130 final _FutureOnValue _onValueCallback; |
| 132 final _FutureErrorTest _errorTestCallback; | 131 final _FutureErrorTest _errorTestCallback; |
| 133 final _FutureOnError _onErrorCallback; | 132 final Function _onErrorCallback; |
| 134 final _FutureAction _whenCompleteActionCallback; | 133 final _FutureAction _whenCompleteActionCallback; |
| 135 | 134 |
| 136 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; | 135 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; |
| 137 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; | 136 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; |
| 138 _FutureOnError get _onError => _isChained ? null : _onErrorCallback; | 137 Function get _onError => _isChained ? null : _onErrorCallback; |
| 139 _FutureAction get _whenCompleteAction | 138 _FutureAction get _whenCompleteAction |
| 140 => _isChained ? null : _whenCompleteActionCallback; | 139 => _isChained ? null : _whenCompleteActionCallback; |
| 141 | 140 |
| 142 _Future() | 141 _Future() |
| 143 : _zone = Zone.current, | 142 : _zone = Zone.current, |
| 144 _onValueCallback = null, _errorTestCallback = null, | 143 _onValueCallback = null, _errorTestCallback = null, |
| 145 _onErrorCallback = null, _whenCompleteActionCallback = null; | 144 _onErrorCallback = null, _whenCompleteActionCallback = null; |
| 146 | 145 |
| 147 /// Valid types for value: `T` or `Future<T>`. | 146 /// Valid types for value: `T` or `Future<T>`. |
| 148 _Future.immediate(value) | 147 _Future.immediate(value) |
| 149 : _zone = Zone.current, | 148 : _zone = Zone.current, |
| 150 _onValueCallback = null, _errorTestCallback = null, | 149 _onValueCallback = null, _errorTestCallback = null, |
| 151 _onErrorCallback = null, _whenCompleteActionCallback = null { | 150 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 152 _asyncComplete(value); | 151 _asyncComplete(value); |
| 153 } | 152 } |
| 154 | 153 |
| 155 _Future.immediateError(var error, [Object stackTrace]) | 154 _Future.immediateError(var error, [Object stackTrace]) |
| 156 : _zone = Zone.current, | 155 : _zone = Zone.current, |
| 157 _onValueCallback = null, _errorTestCallback = null, | 156 _onValueCallback = null, _errorTestCallback = null, |
| 158 _onErrorCallback = null, _whenCompleteActionCallback = null { | 157 _onErrorCallback = null, _whenCompleteActionCallback = null { |
| 159 _asyncCompleteError(error, stackTrace); | 158 _asyncCompleteError(error, stackTrace); |
| 160 } | 159 } |
| 161 | 160 |
| 162 _Future._then(onValueCallback(value), onErrorCallback(e)) | 161 _Future._then(onValueCallback(value), Function onErrorCallback) |
| 163 : _zone = Zone.current, | 162 : _zone = Zone.current, |
| 164 _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback), | 163 _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback), |
| 165 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), | 164 _onErrorCallback = _registerErrorCallback(onErrorCallback), |
| 166 _errorTestCallback = null, | 165 _errorTestCallback = null, |
| 167 _whenCompleteActionCallback = null; | 166 _whenCompleteActionCallback = null; |
| 168 | 167 |
| 169 _Future._catchError(onErrorCallback(e), bool errorTestCallback(e)) | 168 _Future._catchError(Function onErrorCallback, bool errorTestCallback(e)) |
| 170 : _zone = Zone.current, | 169 : _zone = Zone.current, |
| 171 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), | 170 _onErrorCallback = _registerErrorCallback(onErrorCallback), |
| 172 _errorTestCallback = Zone.current.registerUnaryCallback(errorTestCallback)
, | 171 _errorTestCallback = |
| 172 Zone.current.registerUnaryCallback(errorTestCallback), |
| 173 _onValueCallback = null, | 173 _onValueCallback = null, |
| 174 _whenCompleteActionCallback = null; | 174 _whenCompleteActionCallback = null; |
| 175 | 175 |
| 176 _Future._whenComplete(whenCompleteActionCallback()) | 176 _Future._whenComplete(whenCompleteActionCallback()) |
| 177 : _zone = Zone.current, | 177 : _zone = Zone.current, |
| 178 _whenCompleteActionCallback = | 178 _whenCompleteActionCallback = |
| 179 Zone.current.registerCallback(whenCompleteActionCallback), | 179 Zone.current.registerCallback(whenCompleteActionCallback), |
| 180 _onValueCallback = null, | 180 _onValueCallback = null, |
| 181 _errorTestCallback = null, | 181 _errorTestCallback = null, |
| 182 _onErrorCallback = null; | 182 _onErrorCallback = null; |
| 183 | 183 |
| 184 Future then(f(T value), { onError(error) }) { | 184 /// Register the given [errorCallback] in the current zone. |
| 185 static Function _registerErrorCallback(Function errorCallback) { |
| 186 if (errorCallback is ZoneBinaryCallback) { |
| 187 return Zone.current.registerBinaryCallback(errorCallback); |
| 188 } else { |
| 189 return Zone.current.registerUnaryCallback(errorCallback); |
| 190 } |
| 191 } |
| 192 |
| 193 Future then(f(T value), { Function onError }) { |
| 185 _Future result; | 194 _Future result; |
| 186 result = new _Future._then(f, onError); | 195 result = new _Future._then(f, onError); |
| 187 _addListener(result); | 196 _addListener(result); |
| 188 return result; | 197 return result; |
| 189 } | 198 } |
| 190 | 199 |
| 191 Future catchError(f(error), { bool test(error) }) { | 200 Future catchError(Function onError, { bool test(error) }) { |
| 192 _Future result = new _Future._catchError(f, test); | 201 _Future result = new _Future._catchError(onError, test); |
| 193 _addListener(result); | 202 _addListener(result); |
| 194 return result; | 203 return result; |
| 195 } | 204 } |
| 196 | 205 |
| 197 Future<T> whenComplete(action()) { | 206 Future<T> whenComplete(action()) { |
| 198 _Future result = new _Future<T>._whenComplete(action); | 207 _Future result = new _Future<T>._whenComplete(action); |
| 199 _addListener(result); | 208 _addListener(result); |
| 200 return result; | 209 return result; |
| 201 } | 210 } |
| 202 | 211 |
| 203 Stream<T> asStream() => new Stream.fromFuture(this); | 212 Stream<T> asStream() => new Stream.fromFuture(this); |
| 204 | 213 |
| 205 void _markPendingCompletion() { | 214 void _markPendingCompletion() { |
| 206 if (!_mayComplete) throw new StateError("Future already completed"); | 215 if (!_mayComplete) throw new StateError("Future already completed"); |
| 207 _state = _PENDING_COMPLETE; | 216 _state = _PENDING_COMPLETE; |
| 208 } | 217 } |
| 209 | 218 |
| 210 T get _value { | 219 T get _value { |
| 211 assert(_isComplete && _hasValue); | 220 assert(_isComplete && _hasValue); |
| 212 return _resultOrListeners; | 221 return _resultOrListeners; |
| 213 } | 222 } |
| 214 | 223 |
| 215 Object get _error { | 224 _AsyncError get _error { |
| 216 assert(_isComplete && _hasError); | 225 assert(_isComplete && _hasError); |
| 217 return _resultOrListeners; | 226 return _resultOrListeners; |
| 218 } | 227 } |
| 219 | 228 |
| 220 void _setValue(T value) { | 229 void _setValue(T value) { |
| 221 assert(!_isComplete); // But may have a completion pending. | 230 assert(!_isComplete); // But may have a completion pending. |
| 222 _state = _VALUE; | 231 _state = _VALUE; |
| 223 _resultOrListeners = value; | 232 _resultOrListeners = value; |
| 224 } | 233 } |
| 225 | 234 |
| 226 void _setError(Object error) { | 235 void _setError(Object error, StackTrace stackTrace) { |
| 227 assert(!_isComplete); // But may have a completion pending. | 236 assert(!_isComplete); // But may have a completion pending. |
| 228 _state = _ERROR; | 237 _state = _ERROR; |
| 229 _resultOrListeners = error; | 238 _resultOrListeners = new _AsyncError(error, stackTrace); |
| 230 } | 239 } |
| 231 | 240 |
| 232 void _addListener(_Future listener) { | 241 void _addListener(_Future listener) { |
| 233 assert(listener._nextListener == null); | 242 assert(listener._nextListener == null); |
| 234 if (_isComplete) { | 243 if (_isComplete) { |
| 235 // Handle late listeners asynchronously. | 244 // Handle late listeners asynchronously. |
| 236 _zone.scheduleMicrotask(() { | 245 _zone.scheduleMicrotask(() { |
| 237 _propagateToListeners(this, listener); | 246 _propagateToListeners(this, listener); |
| 238 }); | 247 }); |
| 239 } else { | 248 } else { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 268 if (internalFuture._isComplete) { | 277 if (internalFuture._isComplete) { |
| 269 _propagateToListeners(internalFuture, target); | 278 _propagateToListeners(internalFuture, target); |
| 270 } else { | 279 } else { |
| 271 internalFuture._addListener(target); | 280 internalFuture._addListener(target); |
| 272 } | 281 } |
| 273 } else { | 282 } else { |
| 274 source.then((value) { | 283 source.then((value) { |
| 275 assert(target._isChained); | 284 assert(target._isChained); |
| 276 target._complete(value); | 285 target._complete(value); |
| 277 }, | 286 }, |
| 278 onError: (error) { | 287 // TODO(floitsch): eventually we would like to make this non-optional |
| 288 // and dependent on the listeners of the target future. If none of |
| 289 // the target future's listeners want to have the stack trace we don't |
| 290 // need a trace. |
| 291 onError: (error, [stackTrace]) { |
| 279 assert(target._isChained); | 292 assert(target._isChained); |
| 280 target._completeError(error); | 293 target._completeError(error, stackTrace); |
| 281 }); | 294 }); |
| 282 } | 295 } |
| 283 } | 296 } |
| 284 | 297 |
| 285 void _complete(value) { | 298 void _complete(value) { |
| 286 assert(!_isComplete); | 299 assert(!_isComplete); |
| 287 assert(_onValue == null); | 300 assert(_onValue == null); |
| 288 assert(_onError == null); | 301 assert(_onError == null); |
| 289 assert(_whenCompleteAction == null); | 302 assert(_whenCompleteAction == null); |
| 290 assert(_errorTest == null); | 303 assert(_errorTest == null); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 304 assert(_onError == null); | 317 assert(_onError == null); |
| 305 assert(_whenCompleteAction == null); | 318 assert(_whenCompleteAction == null); |
| 306 assert(_errorTest == null); | 319 assert(_errorTest == null); |
| 307 | 320 |
| 308 if (stackTrace != null) { | 321 if (stackTrace != null) { |
| 309 // Force the stack trace onto the error, even if it already had one. | 322 // Force the stack trace onto the error, even if it already had one. |
| 310 _attachStackTrace(error, stackTrace); | 323 _attachStackTrace(error, stackTrace); |
| 311 } | 324 } |
| 312 | 325 |
| 313 _Future listeners = _isChained ? null : _removeListeners(); | 326 _Future listeners = _isChained ? null : _removeListeners(); |
| 314 _setError(error); | 327 _setError(error, stackTrace); |
| 315 _propagateToListeners(this, listeners); | 328 _propagateToListeners(this, listeners); |
| 316 } | 329 } |
| 317 | 330 |
| 318 void _asyncComplete(value) { | 331 void _asyncComplete(value) { |
| 319 assert(!_isComplete); | 332 assert(!_isComplete); |
| 320 assert(_onValue == null); | 333 assert(_onValue == null); |
| 321 assert(_onError == null); | 334 assert(_onError == null); |
| 322 assert(_whenCompleteAction == null); | 335 assert(_whenCompleteAction == null); |
| 323 assert(_errorTest == null); | 336 assert(_errorTest == null); |
| 324 // Two corner cases if the value is a future: | 337 // Two corner cases if the value is a future: |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 * | 404 * |
| 392 * If [runCallback] is true (which should be the default) it executes | 405 * If [runCallback] is true (which should be the default) it executes |
| 393 * the registered action of listeners. If it is `false` then the callback is | 406 * the registered action of listeners. If it is `false` then the callback is |
| 394 * skipped. This is used to complete futures with chained futures. | 407 * skipped. This is used to complete futures with chained futures. |
| 395 */ | 408 */ |
| 396 static void _propagateToListeners(_Future source, _Future listeners) { | 409 static void _propagateToListeners(_Future source, _Future listeners) { |
| 397 while (true) { | 410 while (true) { |
| 398 if (!source._isComplete) return; // Chained future. | 411 if (!source._isComplete) return; // Chained future. |
| 399 bool hasError = source._hasError; | 412 bool hasError = source._hasError; |
| 400 if (hasError && listeners == null) { | 413 if (hasError && listeners == null) { |
| 401 source._zone.handleUncaughtError(source._error); | 414 _AsyncError asyncError = source._error; |
| 415 source._zone.handleUncaughtError( |
| 416 asyncError.error, asyncError.stackTrace); |
| 402 return; | 417 return; |
| 403 } | 418 } |
| 404 if (listeners == null) return; | 419 if (listeners == null) return; |
| 405 _Future listener = listeners; | 420 _Future listener = listeners; |
| 406 if (listener._nextListener != null) { | 421 if (listener._nextListener != null) { |
| 407 // Usually futures only have one listener. If they have several, we | 422 // Usually futures only have one listener. If they have several, we |
| 408 // handle them specially. | 423 // handle them specially. |
| 409 _propagateMultipleListeners(source, listeners); | 424 _propagateMultipleListeners(source, listeners); |
| 410 return; | 425 return; |
| 411 } | 426 } |
| 412 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { | 427 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { |
| 413 // Don't cross zone boundaries with errors. | 428 // Don't cross zone boundaries with errors. |
| 414 source._zone.handleUncaughtError(source._error); | 429 _AsyncError asyncError = source._error; |
| 430 source._zone.handleUncaughtError( |
| 431 asyncError.error, asyncError.stackTrace); |
| 415 return; | 432 return; |
| 416 } | 433 } |
| 417 if (!identical(Zone.current, listener._zone)) { | 434 if (!identical(Zone.current, listener._zone)) { |
| 418 // Run the propagation in the listener's zone to avoid | 435 // Run the propagation in the listener's zone to avoid |
| 419 // zone transitions. The idea is that many chained futures will | 436 // zone transitions. The idea is that many chained futures will |
| 420 // be in the same zone. | 437 // be in the same zone. |
| 421 listener._zone.run(() { | 438 listener._zone.run(() { |
| 422 _propagateToListeners(source, listener); | 439 _propagateToListeners(source, listener); |
| 423 }); | 440 }); |
| 424 return; | 441 return; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 447 var value = source._value; | 464 var value = source._value; |
| 448 if (listener._onValue != null) { | 465 if (listener._onValue != null) { |
| 449 listenerValueOrError = listener._onValue(value); | 466 listenerValueOrError = listener._onValue(value); |
| 450 listenerHasValue = true; | 467 listenerHasValue = true; |
| 451 } else { | 468 } else { |
| 452 // Copy over the value from the source. | 469 // Copy over the value from the source. |
| 453 listenerValueOrError = value; | 470 listenerValueOrError = value; |
| 454 listenerHasValue = true; | 471 listenerHasValue = true; |
| 455 } | 472 } |
| 456 } else { | 473 } else { |
| 457 Object error = source._error; | 474 _AsyncError asyncError = source._error; |
| 458 _FutureErrorTest test = listener._errorTest; | 475 _FutureErrorTest test = listener._errorTest; |
| 459 bool matchesTest = true; | 476 bool matchesTest = true; |
| 460 if (test != null) { | 477 if (test != null) { |
| 461 matchesTest = test(error); | 478 matchesTest = test(asyncError.error); |
| 462 } | 479 } |
| 463 if (matchesTest && listener._onError != null) { | 480 if (matchesTest && listener._onError != null) { |
| 464 listenerValueOrError = listener._onError(error); | 481 Function errorCallback = listener._onError; |
| 482 if (errorCallback is ZoneBinaryCallback) { |
| 483 listenerValueOrError = |
| 484 errorCallback(asyncError.error, asyncError.stackTrace); |
| 485 } else { |
| 486 listenerValueOrError = listener._onError(asyncError.error); |
| 487 } |
| 465 listenerHasValue = true; | 488 listenerHasValue = true; |
| 466 } else { | 489 } else { |
| 467 // Copy over the error from the source. | 490 // Copy over the error from the source. |
| 468 listenerValueOrError = error; | 491 listenerValueOrError = asyncError; |
| 469 listenerHasValue = false; | 492 listenerHasValue = false; |
| 470 } | 493 } |
| 471 } | 494 } |
| 472 | 495 |
| 473 if (listener._whenCompleteAction != null) { | 496 if (listener._whenCompleteAction != null) { |
| 474 var completeResult = listener._whenCompleteAction(); | 497 var completeResult = listener._whenCompleteAction(); |
| 475 if (completeResult is Future) { | 498 if (completeResult is Future) { |
| 476 listener._isChained = true; | 499 listener._isChained = true; |
| 477 completeResult.then((ignored) { | 500 completeResult.then((ignored) { |
| 478 // Try again, but this time don't run the whenComplete callback. | 501 // Try again, but this time don't run the whenComplete callback. |
| 479 _propagateToListeners(source, listener); | 502 _propagateToListeners(source, listener); |
| 480 }, onError: (error) { | 503 }, onError: (error, [stackTrace]) { |
| 481 // When there is an error, we have to make the error the new | 504 // When there is an error, we have to make the error the new |
| 482 // result of the current listener. | 505 // result of the current listener. |
| 483 if (completeResult is! _Future) { | 506 if (completeResult is! _Future) { |
| 484 // This should be a rare case. | 507 // This should be a rare case. |
| 485 completeResult = new _Future(); | 508 completeResult = new _Future(); |
| 486 completeResult._setError(error); | 509 completeResult._setError(error, stackTrace); |
| 487 } | 510 } |
| 488 _propagateToListeners(completeResult, listener); | 511 _propagateToListeners(completeResult, listener); |
| 489 }); | 512 }); |
| 490 isPropagationAborted = true; | 513 isPropagationAborted = true; |
| 491 } | 514 } |
| 492 } | 515 } |
| 493 } catch (e, s) { | 516 } catch (e, s) { |
| 494 // Set the exception as error. | 517 // Set the exception as error unless the error is the same as the |
| 495 listenerValueOrError = _asyncError(e, s); | 518 // original one. |
| 519 if (hasError && identical(source._error.error, e)) { |
| 520 listenerValueOrError = source._error; |
| 521 } else { |
| 522 listenerValueOrError = new _AsyncError(_asyncError(e, s), s); |
| 523 } |
| 496 listenerHasValue = false; | 524 listenerHasValue = false; |
| 497 } | 525 } |
| 498 }); | 526 }); |
| 499 if (isPropagationAborted) return; | 527 if (isPropagationAborted) return; |
| 500 // If the listener's value is a future we need to chain it. | 528 // If the listener's value is a future we need to chain it. |
| 501 if (listenerHasValue && listenerValueOrError is Future) { | 529 if (listenerHasValue && listenerValueOrError is Future) { |
| 502 Future chainSource = listenerValueOrError; | 530 Future chainSource = listenerValueOrError; |
| 503 // Shortcut if the chain-source is already completed. Just continue the | 531 // Shortcut if the chain-source is already completed. Just continue the |
| 504 // loop. | 532 // loop. |
| 505 if (chainSource is _Future && (chainSource as _Future)._isComplete) { | 533 if (chainSource is _Future && (chainSource as _Future)._isComplete) { |
| 506 // propagate the value (simulating a tail call). | 534 // propagate the value (simulating a tail call). |
| 507 listener._isChained = true; | 535 listener._isChained = true; |
| 508 source = chainSource; | 536 source = chainSource; |
| 509 listeners = listener; | 537 listeners = listener; |
| 510 continue; | 538 continue; |
| 511 } | 539 } |
| 512 _chainFutures(chainSource, listener); | 540 _chainFutures(chainSource, listener); |
| 513 return; | 541 return; |
| 514 } | 542 } |
| 515 | 543 |
| 516 if (listenerHasValue) { | 544 if (listenerHasValue) { |
| 517 listeners = listener._removeListeners(); | 545 listeners = listener._removeListeners(); |
| 518 listener._setValue(listenerValueOrError); | 546 listener._setValue(listenerValueOrError); |
| 519 } else { | 547 } else { |
| 520 listeners = listener._removeListeners(); | 548 listeners = listener._removeListeners(); |
| 521 listener._setError(listenerValueOrError); | 549 _AsyncError asyncError = listenerValueOrError; |
| 550 listener._setError(asyncError.error, asyncError.stackTrace); |
| 522 } | 551 } |
| 523 // Prepare for next round. | 552 // Prepare for next round. |
| 524 source = listener; | 553 source = listener; |
| 525 } | 554 } |
| 526 } | 555 } |
| 527 } | 556 } |
| OLD | NEW |