| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of dart.async; | |
| 6 | |
| 7 /** The onValue and onError handlers return either a value or a future */ | |
| 8 typedef dynamic _FutureOnValue<T>(T value); | |
| 9 /** Test used by [Future.catchError] to handle skip some errors. */ | |
| 10 typedef bool _FutureErrorTest(var error); | |
| 11 /** Used by [WhenFuture]. */ | |
| 12 typedef _FutureAction(); | |
| 13 | |
| 14 abstract class _Completer<T> implements Completer<T> { | |
| 15 final _Future<T> future = new _Future<T>(); | |
| 16 | |
| 17 void complete([value]); | |
| 18 | |
| 19 void completeError(Object error, [StackTrace stackTrace]) { | |
| 20 error = _nonNullError(error); | |
| 21 if (!future._mayComplete) throw new StateError("Future already completed"); | |
| 22 AsyncError replacement = Zone.current.errorCallback(error, stackTrace); | |
| 23 if (replacement != null) { | |
| 24 error = _nonNullError(replacement.error); | |
| 25 stackTrace = replacement.stackTrace; | |
| 26 } | |
| 27 _completeError(error, stackTrace); | |
| 28 } | |
| 29 | |
| 30 void _completeError(Object error, StackTrace stackTrace); | |
| 31 | |
| 32 // The future's _isComplete doesn't take into account pending completions. | |
| 33 // We therefore use _mayComplete. | |
| 34 bool get isCompleted => !future._mayComplete; | |
| 35 } | |
| 36 | |
| 37 class _AsyncCompleter<T> extends _Completer<T> { | |
| 38 | |
| 39 void complete([value]) { | |
| 40 if (!future._mayComplete) throw new StateError("Future already completed"); | |
| 41 future._asyncComplete(value); | |
| 42 } | |
| 43 | |
| 44 void _completeError(Object error, StackTrace stackTrace) { | |
| 45 future._asyncCompleteError(error, stackTrace); | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 class _SyncCompleter<T> extends _Completer<T> { | |
| 50 void complete([value]) { | |
| 51 if (!future._mayComplete) throw new StateError("Future already completed"); | |
| 52 future._complete(value); | |
| 53 } | |
| 54 | |
| 55 void _completeError(Object error, StackTrace stackTrace) { | |
| 56 future._completeError(error, stackTrace); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 class _FutureListener { | |
| 61 static const int MASK_VALUE = 1; | |
| 62 static const int MASK_ERROR = 2; | |
| 63 static const int MASK_TEST_ERROR = 4; | |
| 64 static const int MASK_WHENCOMPLETE = 8; | |
| 65 static const int STATE_CHAIN = 0; | |
| 66 static const int STATE_THEN = MASK_VALUE; | |
| 67 static const int STATE_THEN_ONERROR = MASK_VALUE | MASK_ERROR; | |
| 68 static const int STATE_CATCHERROR = MASK_ERROR; | |
| 69 static const int STATE_CATCHERROR_TEST = MASK_ERROR | MASK_TEST_ERROR; | |
| 70 static const int STATE_WHENCOMPLETE = MASK_WHENCOMPLETE; | |
| 71 // Listeners on the same future are linked through this link. | |
| 72 _FutureListener _nextListener = null; | |
| 73 // The future to complete when this listener is activated. | |
| 74 final _Future result; | |
| 75 // Which fields means what. | |
| 76 final int state; | |
| 77 // Used for then/whenDone callback and error test | |
| 78 final Function callback; | |
| 79 // Used for error callbacks. | |
| 80 final Function errorCallback; | |
| 81 | |
| 82 _FutureListener.then(this.result, | |
| 83 _FutureOnValue onValue, Function errorCallback) | |
| 84 : callback = onValue, | |
| 85 errorCallback = errorCallback, | |
| 86 state = (errorCallback == null) ? STATE_THEN : STATE_THEN_ONERROR; | |
| 87 | |
| 88 _FutureListener.catchError(this.result, | |
| 89 this.errorCallback, _FutureErrorTest test) | |
| 90 : callback = test, | |
| 91 state = (test == null) ? STATE_CATCHERROR : STATE_CATCHERROR_TEST; | |
| 92 | |
| 93 _FutureListener.whenComplete(this.result, _FutureAction onComplete) | |
| 94 : callback = onComplete, | |
| 95 errorCallback = null, | |
| 96 state = STATE_WHENCOMPLETE; | |
| 97 | |
| 98 _FutureListener.chain(this.result) | |
| 99 : callback = null, | |
| 100 errorCallback = null, | |
| 101 state = STATE_CHAIN; | |
| 102 | |
| 103 Zone get _zone => result._zone; | |
| 104 | |
| 105 bool get handlesValue => (state & MASK_VALUE != 0); | |
| 106 bool get handlesError => (state & MASK_ERROR != 0); | |
| 107 bool get hasErrorTest => (state == STATE_CATCHERROR_TEST); | |
| 108 bool get handlesComplete => (state == STATE_WHENCOMPLETE); | |
| 109 | |
| 110 _FutureOnValue get _onValue { | |
| 111 assert(handlesValue); | |
| 112 return callback; | |
| 113 } | |
| 114 Function get _onError => errorCallback; | |
| 115 _FutureErrorTest get _errorTest { | |
| 116 assert(hasErrorTest); | |
| 117 return callback; | |
| 118 } | |
| 119 _FutureAction get _whenCompleteAction { | |
| 120 assert(handlesComplete); | |
| 121 return callback; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 class _Future<T> implements Future<T> { | |
| 126 /// Initial state, waiting for a result. In this state, the | |
| 127 /// [resultOrListeners] field holds a single-linked list of | |
| 128 /// [_FutureListener] listeners. | |
| 129 static const int _INCOMPLETE = 0; | |
| 130 /// Pending completion. Set when completed using [_asyncComplete] or | |
| 131 /// [_asyncCompleteError]. It is an error to try to complete it again. | |
| 132 /// [resultOrListeners] holds listeners. | |
| 133 static const int _PENDING_COMPLETE = 1; | |
| 134 /// The future has been chained to another future. The result of that | |
| 135 /// other future becomes the result of this future as well. | |
| 136 // TODO(floitsch): we don't really need a special "_CHAINED" state. We could | |
| 137 // just use the PENDING_COMPLETE state instead. | |
| 138 static const int _CHAINED = 2; | |
| 139 /// The future has been completed with a value result. | |
| 140 static const int _VALUE = 4; | |
| 141 /// The future has been completed with an error result. | |
| 142 static const int _ERROR = 8; | |
| 143 | |
| 144 /** Whether the future is complete, and as what. */ | |
| 145 int _state = _INCOMPLETE; | |
| 146 | |
| 147 /** | |
| 148 * Zone that the future was completed from. | |
| 149 * This is the zone that an error result belongs to. | |
| 150 * | |
| 151 * Until the future is completed, the field may hold the zone that | |
| 152 * listener callbacks used to create this future should be run in. | |
| 153 */ | |
| 154 final Zone _zone = Zone.current; | |
| 155 | |
| 156 /** | |
| 157 * Either the result, a list of listeners or another future. | |
| 158 * | |
| 159 * The result of the future is either a value or an error. | |
| 160 * A result is only stored when the future has completed. | |
| 161 * | |
| 162 * The listeners is an internally linked list of [_FutureListener]s. | |
| 163 * Listeners are only remembered while the future is not yet complete, | |
| 164 * and it is not chained to another future. | |
| 165 * | |
| 166 * 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 | |
| 168 * will complete with the same result. | |
| 169 * 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 */ | |
| 176 var _resultOrListeners; | |
| 177 | |
| 178 _Future(); | |
| 179 | |
| 180 /// Valid types for value: `T` or `Future<T>`. | |
| 181 _Future.immediate(value) { | |
| 182 _asyncComplete(value); | |
| 183 } | |
| 184 | |
| 185 _Future.immediateError(var error, [StackTrace stackTrace]) { | |
| 186 _asyncCompleteError(error, stackTrace); | |
| 187 } | |
| 188 | |
| 189 bool get _mayComplete => _state == _INCOMPLETE; | |
| 190 bool get _isChained => _state == _CHAINED; | |
| 191 bool get _isComplete => _state >= _VALUE; | |
| 192 bool get _hasValue => _state == _VALUE; | |
| 193 bool get _hasError => _state == _ERROR; | |
| 194 | |
| 195 set _isChained(bool value) { | |
| 196 if (value) { | |
| 197 assert(!_isComplete); | |
| 198 _state = _CHAINED; | |
| 199 } else { | |
| 200 assert(_isChained); | |
| 201 _state = _INCOMPLETE; | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 Future then(f(T value), { Function onError }) { | |
| 206 _Future result = new _Future(); | |
| 207 if (!identical(result._zone, _ROOT_ZONE)) { | |
| 208 f = result._zone.registerUnaryCallback(f); | |
| 209 if (onError != null) { | |
| 210 onError = _registerErrorHandler(onError, result._zone); | |
| 211 } | |
| 212 } | |
| 213 _addListener(new _FutureListener.then(result, f, onError)); | |
| 214 return result; | |
| 215 } | |
| 216 | |
| 217 Future catchError(Function onError, { bool test(error) }) { | |
| 218 _Future result = new _Future(); | |
| 219 if (!identical(result._zone, _ROOT_ZONE)) { | |
| 220 onError = _registerErrorHandler(onError, result._zone); | |
| 221 if (test != null) test = result._zone.registerUnaryCallback(test); | |
| 222 } | |
| 223 _addListener(new _FutureListener.catchError(result, onError, test)); | |
| 224 return result; | |
| 225 } | |
| 226 | |
| 227 Future<T> whenComplete(action()) { | |
| 228 _Future result = new _Future<T>(); | |
| 229 if (!identical(result._zone, _ROOT_ZONE)) { | |
| 230 action = result._zone.registerCallback(action); | |
| 231 } | |
| 232 _addListener(new _FutureListener.whenComplete(result, action)); | |
| 233 return result; | |
| 234 } | |
| 235 | |
| 236 Stream<T> asStream() => new Stream.fromFuture(this); | |
| 237 | |
| 238 void _markPendingCompletion() { | |
| 239 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 240 _state = _PENDING_COMPLETE; | |
| 241 } | |
| 242 | |
| 243 T get _value { | |
| 244 assert(_isComplete && _hasValue); | |
| 245 return _resultOrListeners; | |
| 246 } | |
| 247 | |
| 248 AsyncError get _error { | |
| 249 assert(_isComplete && _hasError); | |
| 250 return _resultOrListeners; | |
| 251 } | |
| 252 | |
| 253 void _setValue(T value) { | |
| 254 assert(!_isComplete); // But may have a completion pending. | |
| 255 _state = _VALUE; | |
| 256 _resultOrListeners = value; | |
| 257 } | |
| 258 | |
| 259 void _setErrorObject(AsyncError error) { | |
| 260 assert(!_isComplete); // But may have a completion pending. | |
| 261 _state = _ERROR; | |
| 262 _resultOrListeners = error; | |
| 263 } | |
| 264 | |
| 265 void _setError(Object error, StackTrace stackTrace) { | |
| 266 _setErrorObject(new AsyncError(error, stackTrace)); | |
| 267 } | |
| 268 | |
| 269 void _addListener(_FutureListener listener) { | |
| 270 assert(listener._nextListener == null); | |
| 271 if (_isComplete) { | |
| 272 // Handle late listeners asynchronously. | |
| 273 _zone.scheduleMicrotask(() { | |
| 274 _propagateToListeners(this, listener); | |
| 275 }); | |
| 276 } else { | |
| 277 listener._nextListener = _resultOrListeners; | |
| 278 _resultOrListeners = listener; | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 _FutureListener _removeListeners() { | |
| 283 // Reverse listeners before returning them, so the resulting list is in | |
| 284 // subscription order. | |
| 285 assert(!_isComplete); | |
| 286 _FutureListener current = _resultOrListeners; | |
| 287 _resultOrListeners = null; | |
| 288 _FutureListener prev = null; | |
| 289 while (current != null) { | |
| 290 _FutureListener next = current._nextListener; | |
| 291 current._nextListener = prev; | |
| 292 prev = current; | |
| 293 current = next; | |
| 294 } | |
| 295 return prev; | |
| 296 } | |
| 297 | |
| 298 // Take the value (when completed) of source and complete target with that | |
| 299 // value (or error). This function can chain all Futures, but is slower | |
| 300 // for _Future than _chainCoreFuture - Use _chainCoreFuture in that case. | |
| 301 static void _chainForeignFuture(Future source, _Future target) { | |
| 302 assert(!target._isComplete); | |
| 303 assert(source is! _Future); | |
| 304 | |
| 305 // Mark the target as chained (and as such half-completed). | |
| 306 target._isChained = true; | |
| 307 source.then((value) { | |
| 308 assert(target._isChained); | |
| 309 target._completeWithValue(value); | |
| 310 }, | |
| 311 // TODO(floitsch): eventually we would like to make this non-optional | |
| 312 // and dependent on the listeners of the target future. If none of | |
| 313 // the target future's listeners want to have the stack trace we don't | |
| 314 // need a trace. | |
| 315 onError: (error, [stackTrace]) { | |
| 316 assert(target._isChained); | |
| 317 target._completeError(error, stackTrace); | |
| 318 }); | |
| 319 } | |
| 320 | |
| 321 // Take the value (when completed) of source and complete target with that | |
| 322 // value (or error). This function expects that source is a _Future. | |
| 323 static void _chainCoreFuture(_Future source, _Future target) { | |
| 324 assert(!target._isComplete); | |
| 325 assert(source is _Future); | |
| 326 | |
| 327 // Mark the target as chained (and as such half-completed). | |
| 328 target._isChained = true; | |
| 329 _FutureListener listener = new _FutureListener.chain(target); | |
| 330 if (source._isComplete) { | |
| 331 _propagateToListeners(source, listener); | |
| 332 } else { | |
| 333 source._addListener(listener); | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 void _complete(value) { | |
| 338 assert(!_isComplete); | |
| 339 if (value is Future) { | |
| 340 if (value is _Future) { | |
| 341 _chainCoreFuture(value, this); | |
| 342 } else { | |
| 343 _chainForeignFuture(value, this); | |
| 344 } | |
| 345 } else { | |
| 346 _FutureListener listeners = _removeListeners(); | |
| 347 _setValue(value); | |
| 348 _propagateToListeners(this, listeners); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 void _completeWithValue(value) { | |
| 353 assert(!_isComplete); | |
| 354 assert(value is! Future); | |
| 355 | |
| 356 _FutureListener listeners = _removeListeners(); | |
| 357 _setValue(value); | |
| 358 _propagateToListeners(this, listeners); | |
| 359 } | |
| 360 | |
| 361 void _completeError(error, [StackTrace stackTrace]) { | |
| 362 assert(!_isComplete); | |
| 363 | |
| 364 _FutureListener listeners = _removeListeners(); | |
| 365 _setError(error, stackTrace); | |
| 366 _propagateToListeners(this, listeners); | |
| 367 } | |
| 368 | |
| 369 void _asyncComplete(value) { | |
| 370 assert(!_isComplete); | |
| 371 // Two corner cases if the value is a future: | |
| 372 // 1. the future is already completed and an error. | |
| 373 // 2. the future is not yet completed but might become an error. | |
| 374 // The first case means that we must not immediately complete the Future, | |
| 375 // as our code would immediately start propagating the error without | |
| 376 // giving the time to install error-handlers. | |
| 377 // However the second case requires us to deal with the value immediately. | |
| 378 // Otherwise the value could complete with an error and report an | |
| 379 // unhandled error, even though we know we are already going to listen to | |
| 380 // it. | |
| 381 | |
| 382 if (value == null) { | |
| 383 // No checks for `null`. | |
| 384 } else if (value is Future) { | |
| 385 // Assign to typed variables so we get earlier checks in checked mode. | |
| 386 Future<T> typedFuture = value; | |
| 387 if (typedFuture is _Future) { | |
| 388 _Future<T> coreFuture = typedFuture; | |
| 389 if (coreFuture._isComplete && coreFuture._hasError) { | |
| 390 // Case 1 from above. Delay completion to enable the user to register | |
| 391 // callbacks. | |
| 392 _markPendingCompletion(); | |
| 393 _zone.scheduleMicrotask(() { | |
| 394 _chainCoreFuture(coreFuture, this); | |
| 395 }); | |
| 396 } else { | |
| 397 _chainCoreFuture(coreFuture, this); | |
| 398 } | |
| 399 } else { | |
| 400 // Case 2 from above. Chain the future immidiately. | |
| 401 // Note that we are still completing asynchronously (through | |
| 402 // _chainForeignFuture).. | |
| 403 _chainForeignFuture(typedFuture, this); | |
| 404 } | |
| 405 return; | |
| 406 } else { | |
| 407 T typedValue = value; | |
| 408 } | |
| 409 | |
| 410 _markPendingCompletion(); | |
| 411 _zone.scheduleMicrotask(() { | |
| 412 _completeWithValue(value); | |
| 413 }); | |
| 414 } | |
| 415 | |
| 416 void _asyncCompleteError(error, StackTrace stackTrace) { | |
| 417 assert(!_isComplete); | |
| 418 | |
| 419 _markPendingCompletion(); | |
| 420 _zone.scheduleMicrotask(() { | |
| 421 _completeError(error, stackTrace); | |
| 422 }); | |
| 423 } | |
| 424 | |
| 425 /** | |
| 426 * Propagates the value/error of [source] to its [listeners], executing the | |
| 427 * listeners' callbacks. | |
| 428 */ | |
| 429 static void _propagateToListeners(_Future source, _FutureListener listeners) { | |
| 430 while (true) { | |
| 431 assert(source._isComplete); | |
| 432 bool hasError = source._hasError; | |
| 433 if (listeners == null) { | |
| 434 if (hasError) { | |
| 435 AsyncError asyncError = source._error; | |
| 436 source._zone.handleUncaughtError( | |
| 437 asyncError.error, asyncError.stackTrace); | |
| 438 } | |
| 439 return; | |
| 440 } | |
| 441 // Usually futures only have one listener. If they have several, we | |
| 442 // call handle them separately in recursive calls, continuing | |
| 443 // here only when there is only one listener left. | |
| 444 while (listeners._nextListener != null) { | |
| 445 _FutureListener listener = listeners; | |
| 446 listeners = listener._nextListener; | |
| 447 listener._nextListener = null; | |
| 448 _propagateToListeners(source, listener); | |
| 449 } | |
| 450 _FutureListener listener = listeners; | |
| 451 // Do the actual propagation. | |
| 452 // Set initial state of listenerHasValue and listenerValueOrError. These | |
| 453 // variables are updated, with the outcome of potential callbacks. | |
| 454 bool listenerHasValue = true; | |
| 455 final sourceValue = hasError ? null : source._value; | |
| 456 var listenerValueOrError = sourceValue; | |
| 457 // Set to true if a whenComplete needs to wait for a future. | |
| 458 // The whenComplete action will resume the propagation by itself. | |
| 459 bool isPropagationAborted = false; | |
| 460 // TODO(floitsch): mark the listener as pending completion. Currently | |
| 461 // we can't do this, since the markPendingCompletion verifies that | |
| 462 // the future is not already marked (or chained). | |
| 463 // Only if we either have an error or callbacks, go into this, somewhat | |
| 464 // expensive, branch. Here we'll enter/leave the zone. Many futures | |
| 465 // doesn't have callbacks, so this is a significant optimization. | |
| 466 if (hasError || (listener.handlesValue || listener.handlesComplete)) { | |
| 467 Zone zone = listener._zone; | |
| 468 if (hasError && !source._zone.inSameErrorZone(zone)) { | |
| 469 // Don't cross zone boundaries with errors. | |
| 470 AsyncError asyncError = source._error; | |
| 471 source._zone.handleUncaughtError( | |
| 472 asyncError.error, asyncError.stackTrace); | |
| 473 return; | |
| 474 } | |
| 475 | |
| 476 Zone oldZone; | |
| 477 if (!identical(Zone.current, zone)) { | |
| 478 // Change zone if it's not current. | |
| 479 oldZone = Zone._enter(zone); | |
| 480 } | |
| 481 | |
| 482 bool handleValueCallback() { | |
| 483 try { | |
| 484 listenerValueOrError = zone.runUnary(listener._onValue, | |
| 485 sourceValue); | |
| 486 return true; | |
| 487 } catch (e, s) { | |
| 488 listenerValueOrError = new AsyncError(e, s); | |
| 489 return false; | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 void handleError() { | |
| 494 AsyncError asyncError = source._error; | |
| 495 bool matchesTest = true; | |
| 496 if (listener.hasErrorTest) { | |
| 497 _FutureErrorTest test = listener._errorTest; | |
| 498 try { | |
| 499 matchesTest = zone.runUnary(test, asyncError.error); | |
| 500 } catch (e, s) { | |
| 501 listenerValueOrError = identical(asyncError.error, e) ? | |
| 502 asyncError : new AsyncError(e, s); | |
| 503 listenerHasValue = false; | |
| 504 return; | |
| 505 } | |
| 506 } | |
| 507 Function errorCallback = listener._onError; | |
| 508 if (matchesTest && errorCallback != null) { | |
| 509 try { | |
| 510 if (errorCallback is ZoneBinaryCallback) { | |
| 511 listenerValueOrError = zone.runBinary(errorCallback, | |
| 512 asyncError.error, | |
| 513 asyncError.stackTrace); | |
| 514 } else { | |
| 515 listenerValueOrError = zone.runUnary(errorCallback, | |
| 516 asyncError.error); | |
| 517 } | |
| 518 } catch (e, s) { | |
| 519 listenerValueOrError = identical(asyncError.error, e) ? | |
| 520 asyncError : new AsyncError(e, s); | |
| 521 listenerHasValue = false; | |
| 522 return; | |
| 523 } | |
| 524 listenerHasValue = true; | |
| 525 } else { | |
| 526 // Copy over the error from the source. | |
| 527 listenerValueOrError = asyncError; | |
| 528 listenerHasValue = false; | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 void handleWhenCompleteCallback() { | |
| 533 var completeResult; | |
| 534 try { | |
| 535 completeResult = zone.run(listener._whenCompleteAction); | |
| 536 } catch (e, s) { | |
| 537 if (hasError && identical(source._error.error, e)) { | |
| 538 listenerValueOrError = source._error; | |
| 539 } else { | |
| 540 listenerValueOrError = new AsyncError(e, s); | |
| 541 } | |
| 542 listenerHasValue = false; | |
| 543 return; | |
| 544 } | |
| 545 if (completeResult is Future) { | |
| 546 _Future result = listener.result; | |
| 547 result._isChained = true; | |
| 548 isPropagationAborted = true; | |
| 549 completeResult.then((ignored) { | |
| 550 _propagateToListeners(source, new _FutureListener.chain(result)); | |
| 551 }, onError: (error, [stackTrace]) { | |
| 552 // When there is an error, we have to make the error the new | |
| 553 // result of the current listener. | |
| 554 if (completeResult is! _Future) { | |
| 555 // This should be a rare case. | |
| 556 completeResult = new _Future(); | |
| 557 completeResult._setError(error, stackTrace); | |
| 558 } | |
| 559 _propagateToListeners(completeResult, | |
| 560 new _FutureListener.chain(result)); | |
| 561 }); | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 if (!hasError) { | |
| 566 if (listener.handlesValue) { | |
| 567 listenerHasValue = handleValueCallback(); | |
| 568 } | |
| 569 } else { | |
| 570 handleError(); | |
| 571 } | |
| 572 if (listener.handlesComplete) { | |
| 573 handleWhenCompleteCallback(); | |
| 574 } | |
| 575 // If we changed zone, oldZone will not be null. | |
| 576 if (oldZone != null) Zone._leave(oldZone); | |
| 577 | |
| 578 if (isPropagationAborted) return; | |
| 579 // If the listener's value is a future we need to chain it. Note that | |
| 580 // this can only happen if there is a callback. Since 'is' checks | |
| 581 // can be expensive, we're trying to avoid it. | |
| 582 if (listenerHasValue && | |
| 583 !identical(sourceValue, listenerValueOrError) && | |
| 584 listenerValueOrError is Future) { | |
| 585 Future chainSource = listenerValueOrError; | |
| 586 // Shortcut if the chain-source is already completed. Just continue | |
| 587 // the loop. | |
| 588 _Future result = listener.result; | |
| 589 if (chainSource is _Future) { | |
| 590 if (chainSource._isComplete) { | |
| 591 // propagate the value (simulating a tail call). | |
| 592 result._isChained = true; | |
| 593 source = chainSource; | |
| 594 listeners = new _FutureListener.chain(result); | |
| 595 continue; | |
| 596 } else { | |
| 597 _chainCoreFuture(chainSource, result); | |
| 598 } | |
| 599 } else { | |
| 600 _chainForeignFuture(chainSource, result); | |
| 601 } | |
| 602 return; | |
| 603 } | |
| 604 } | |
| 605 _Future result = listener.result; | |
| 606 listeners = result._removeListeners(); | |
| 607 if (listenerHasValue) { | |
| 608 result._setValue(listenerValueOrError); | |
| 609 } else { | |
| 610 AsyncError asyncError = listenerValueOrError; | |
| 611 result._setErrorObject(asyncError); | |
| 612 } | |
| 613 // Prepare for next round. | |
| 614 source = result; | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 Future timeout(Duration timeLimit, {onTimeout()}) { | |
| 619 if (_isComplete) return new _Future.immediate(this); | |
| 620 _Future result = new _Future(); | |
| 621 Timer timer; | |
| 622 if (onTimeout == null) { | |
| 623 timer = new Timer(timeLimit, () { | |
| 624 result._completeError(new TimeoutException("Future not completed", | |
| 625 timeLimit)); | |
| 626 }); | |
| 627 } else { | |
| 628 Zone zone = Zone.current; | |
| 629 onTimeout = zone.registerCallback(onTimeout); | |
| 630 timer = new Timer(timeLimit, () { | |
| 631 try { | |
| 632 result._complete(zone.run(onTimeout)); | |
| 633 } catch (e, s) { | |
| 634 result._completeError(e, s); | |
| 635 } | |
| 636 }); | |
| 637 } | |
| 638 this.then((T v) { | |
| 639 if (timer.isActive) { | |
| 640 timer.cancel(); | |
| 641 result._completeWithValue(v); | |
| 642 } | |
| 643 }, onError: (e, s) { | |
| 644 if (timer.isActive) { | |
| 645 timer.cancel(); | |
| 646 result._completeError(e, s); | |
| 647 } | |
| 648 }); | |
| 649 return result; | |
| 650 } | |
| 651 } | |
| OLD | NEW |