| 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); |
| 11 /** Used by [WhenFuture]. */ | 11 /** Used by [WhenFuture]. */ |
| 12 typedef _FutureAction(); | 12 typedef _FutureAction(); |
| 13 | 13 |
| 14 const bool _GUARDED = true; | |
| 15 const bool _UNGUARDED = false; | |
| 16 | |
| 17 abstract class _Completer<T> implements Completer<T> { | 14 abstract class _Completer<T> implements Completer<T> { |
| 18 final _Future<T> future = new _Future<T>(); | 15 final _Future<T> future = new _Future<T>(); |
| 19 | 16 |
| 20 void complete([value]); | 17 void complete([value]); |
| 21 | 18 |
| 22 void completeError(Object error, [StackTrace stackTrace]) { | 19 void completeError(Object error, [StackTrace stackTrace]) { |
| 23 error = _nonNullError(error); | 20 error = _nonNullError(error); |
| 24 if (!future._mayComplete) throw new StateError("Future already completed"); | 21 if (!future._mayComplete) throw new StateError("Future already completed"); |
| 25 AsyncError replacement = Zone.current.errorCallback(error, stackTrace); | 22 AsyncError replacement = Zone.current.errorCallback(error, stackTrace); |
| 26 if (replacement != null) { | 23 if (replacement != null) { |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 | 264 |
| 268 void _setError(Object error, StackTrace stackTrace) { | 265 void _setError(Object error, StackTrace stackTrace) { |
| 269 _setErrorObject(new AsyncError(error, stackTrace)); | 266 _setErrorObject(new AsyncError(error, stackTrace)); |
| 270 } | 267 } |
| 271 | 268 |
| 272 void _addListener(_FutureListener listener) { | 269 void _addListener(_FutureListener listener) { |
| 273 assert(listener._nextListener == null); | 270 assert(listener._nextListener == null); |
| 274 if (_isComplete) { | 271 if (_isComplete) { |
| 275 // Handle late listeners asynchronously. | 272 // Handle late listeners asynchronously. |
| 276 _zone.scheduleMicrotask(() { | 273 _zone.scheduleMicrotask(() { |
| 277 _propagateToFutures(this, listener, _UNGUARDED); | 274 _propagateToListeners(this, listener); |
| 278 }); | 275 }); |
| 279 } else { | 276 } else { |
| 280 listener._nextListener = _resultOrListeners; | 277 listener._nextListener = _resultOrListeners; |
| 281 _resultOrListeners = listener; | 278 _resultOrListeners = listener; |
| 282 } | 279 } |
| 283 } | 280 } |
| 284 | 281 |
| 285 _FutureListener _removeListeners() { | 282 _FutureListener _removeListeners() { |
| 286 // Reverse listeners before returning them, so the resulting list is in | 283 // Reverse listeners before returning them, so the resulting list is in |
| 287 // subscription order. | 284 // subscription order. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 // Take the value (when completed) of source and complete target with that | 321 // Take the value (when completed) of source and complete target with that |
| 325 // value (or error). This function expects that source is a _Future. | 322 // value (or error). This function expects that source is a _Future. |
| 326 static void _chainCoreFuture(_Future source, _Future target) { | 323 static void _chainCoreFuture(_Future source, _Future target) { |
| 327 assert(!target._isComplete); | 324 assert(!target._isComplete); |
| 328 assert(source is _Future); | 325 assert(source is _Future); |
| 329 | 326 |
| 330 // Mark the target as chained (and as such half-completed). | 327 // Mark the target as chained (and as such half-completed). |
| 331 target._isChained = true; | 328 target._isChained = true; |
| 332 _FutureListener listener = new _FutureListener.chain(target); | 329 _FutureListener listener = new _FutureListener.chain(target); |
| 333 if (source._isComplete) { | 330 if (source._isComplete) { |
| 334 _propagateToFutures(source, listener, _GUARDED); | 331 _propagateToListeners(source, listener); |
| 335 } else { | 332 } else { |
| 336 source._addListener(listener); | 333 source._addListener(listener); |
| 337 } | 334 } |
| 338 } | 335 } |
| 339 | 336 |
| 340 void _complete(value) { | 337 void _complete(value) { |
| 341 assert(!_isComplete); | 338 assert(!_isComplete); |
| 342 if (value is Future) { | 339 if (value is Future) { |
| 343 if (value is _Future) { | 340 if (value is _Future) { |
| 344 _chainCoreFuture(value, this); | 341 _chainCoreFuture(value, this); |
| 345 } else { | 342 } else { |
| 346 _chainForeignFuture(value, this); | 343 _chainForeignFuture(value, this); |
| 347 } | 344 } |
| 348 } else { | 345 } else { |
| 349 _FutureListener listeners = _removeListeners(); | 346 _FutureListener listeners = _removeListeners(); |
| 350 _setValue(value); | 347 _setValue(value); |
| 351 _propagateToFutures(this, listeners, _GUARDED); | 348 _propagateToListeners(this, listeners); |
| 352 } | 349 } |
| 353 } | 350 } |
| 354 | 351 |
| 355 void _completeWithValue(value) { | 352 void _completeWithValue(value) { |
| 356 assert(!_isComplete); | 353 assert(!_isComplete); |
| 357 assert(value is! Future); | 354 assert(value is! Future); |
| 358 | 355 |
| 359 _FutureListener listeners = _removeListeners(); | 356 _FutureListener listeners = _removeListeners(); |
| 360 _setValue(value); | 357 _setValue(value); |
| 361 _propagateToFutures(this, listeners, _GUARDED); | 358 _propagateToListeners(this, listeners); |
| 362 } | 359 } |
| 363 | 360 |
| 364 void _completeError(error, [StackTrace stackTrace]) { | 361 void _completeError(error, [StackTrace stackTrace]) { |
| 365 assert(!_isComplete); | 362 assert(!_isComplete); |
| 366 | 363 |
| 367 _FutureListener listeners = _removeListeners(); | 364 _FutureListener listeners = _removeListeners(); |
| 368 _setError(error, stackTrace); | 365 _setError(error, stackTrace); |
| 369 _propagateToFutures(this, listeners, _GUARDED); | 366 _propagateToListeners(this, listeners); |
| 370 } | 367 } |
| 371 | 368 |
| 372 void _asyncComplete(value) { | 369 void _asyncComplete(value) { |
| 373 assert(!_isComplete); | 370 assert(!_isComplete); |
| 374 // Two corner cases if the value is a future: | 371 // Two corner cases if the value is a future: |
| 375 // 1. the future is already completed and an error. | 372 // 1. the future is already completed and an error. |
| 376 // 2. the future is not yet completed but might become an error. | 373 // 2. the future is not yet completed but might become an error. |
| 377 // The first case means that we must not immediately complete the Future, | 374 // The first case means that we must not immediately complete the Future, |
| 378 // as our code would immediately start propagating the error without | 375 // as our code would immediately start propagating the error without |
| 379 // giving the time to install error-handlers. | 376 // giving the time to install error-handlers. |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 void _asyncCompleteError(error, StackTrace stackTrace) { | 416 void _asyncCompleteError(error, StackTrace stackTrace) { |
| 420 assert(!_isComplete); | 417 assert(!_isComplete); |
| 421 | 418 |
| 422 _markPendingCompletion(); | 419 _markPendingCompletion(); |
| 423 _zone.scheduleMicrotask(() { | 420 _zone.scheduleMicrotask(() { |
| 424 _completeError(error, stackTrace); | 421 _completeError(error, stackTrace); |
| 425 }); | 422 }); |
| 426 } | 423 } |
| 427 | 424 |
| 428 /** | 425 /** |
| 429 * Stack implemented by linked list of [_PendingFutureListeners]. | |
| 430 * | |
| 431 * Each `_PendingFutureListeners` have on source future and one or more | |
| 432 * listeners on that future (linked internally through | |
| 433 * [_FutureListener._nextListener]). | |
| 434 * | |
| 435 * While propagating future results to listeners, pending listeners are | |
| 436 * stored on this stack. | |
| 437 */ | |
| 438 static _PendingFutureListeners _pendingFutureListeners = null; | |
| 439 | |
| 440 /** | |
| 441 * Handle an uncaught async error. | |
| 442 * | |
| 443 * If the [guarded] flag is `true`, or the future's zone has an error | |
| 444 * handler, the uncaught error is passed to the zone's error handler. | |
| 445 * Otherwise, the uncaught error is thrown immediately, and is expected | |
| 446 * to propagate to the event loop. (If it can't propagate to the event | |
| 447 * loop, `guarded` should be true). | |
| 448 */ | |
| 449 static void _throwUncaughtError(_Future source, bool guarded) { | |
| 450 assert(source._hasError); | |
| 451 AsyncError asyncError = source._error; | |
| 452 Zone zone = source._zone; | |
| 453 if (guarded || !identical(zone.errorZone, _ROOT_ZONE)) { | |
| 454 zone.handleUncaughtError(asyncError.error, asyncError.stackTrace); | |
| 455 if (_pendingFutureListeners != null) { | |
| 456 _schedulePriorityAsyncCallback(_propagateToFutures); | |
| 457 } | |
| 458 return; | |
| 459 } | |
| 460 if (_pendingFutureListeners != null) { | |
| 461 _schedulePriorityAsyncCallback(_propagateToFutures); | |
| 462 } | |
| 463 throw new _UncaughtAsyncError(asyncError.error, asyncError.stackTrace); | |
| 464 } | |
| 465 | |
| 466 /** | |
| 467 * Propagates the value/error of [source] to its [listeners], executing the | 426 * Propagates the value/error of [source] to its [listeners], executing the |
| 468 * listeners' callbacks. | 427 * listeners' callbacks. |
| 469 * | |
| 470 * The [guarded] flag should be set when the function isn't being called in | |
| 471 * tail position of an event, or if simply throwing an uncaught error is | |
| 472 * otherwise not acceptable. | |
| 473 * | |
| 474 * Parameters are optional to allow using [_propagateToFutures] directly | |
| 475 * as a scheduled function. In that case, it takes its sources from | |
| 476 * [_pendingFutureListeners]. | |
| 477 */ | 428 */ |
| 478 static void _propagateToFutures([_Future source, | 429 static void _propagateToListeners(_Future source, _FutureListener listeners) { |
| 479 _FutureListener listeners, | |
| 480 bool guarded = _UNGUARDED]) { | |
| 481 while (true) { | 430 while (true) { |
| 482 if (source == null) { | 431 assert(source._isComplete); |
| 483 // Pop source and listeners from _pendingFutureListeners. | 432 bool hasError = source._hasError; |
| 484 // This always sets `listeners` to be a single listener. | 433 if (listeners == null) { |
| 485 if (_pendingFutureListeners == null) return; | 434 if (hasError) { |
| 486 _PendingFutureListeners pending = _pendingFutureListeners; | 435 AsyncError asyncError = source._error; |
| 487 // Read top-most source and listener. | 436 source._zone.handleUncaughtError( |
| 488 source = pending.source; | 437 asyncError.error, asyncError.stackTrace); |
| 489 listeners = pending.listeners; | |
| 490 // Remove the top-most listener. If it's the last listener for | |
| 491 // source, go to the next _PendingFutureListeners block. | |
| 492 if (listeners != null && listeners._nextListener != null) { | |
| 493 pending.listeners = listeners._nextListener; | |
| 494 listeners._nextListener = null; | |
| 495 } else { | |
| 496 _pendingFutureListeners = pending.next; | |
| 497 } | 438 } |
| 498 } else { | 439 return; |
| 499 // Check if there are zero listeners, or more than one listener. | |
| 500 assert(source._isComplete); | |
| 501 if (listeners == null) { | |
| 502 // If zero listeners, and source has an error, consider the error | |
| 503 // uncaught. | |
| 504 if (source._hasError) { | |
| 505 _throwUncaughtError(source, guarded); | |
| 506 return; | |
| 507 } | |
| 508 if (_pendingFutureListeners == null) return; | |
| 509 source = null; | |
| 510 continue; | |
| 511 } else if (listeners._nextListener != null) { | |
| 512 // Usually futures only have one listener. | |
| 513 // If they have several, we put the rest on the pending listener | |
| 514 // stack and handle them later. | |
| 515 _pendingFutureListeners = | |
| 516 new _PendingFutureListeners(source, listeners._nextListener, | |
| 517 _pendingFutureListeners); | |
| 518 listeners._nextListener = null; | |
| 519 } | |
| 520 } | 440 } |
| 521 assert(source != null); | 441 // Usually futures only have one listener. If they have several, we |
| 522 assert(listeners != null); | 442 // call handle them separately in recursive calls, continuing |
| 523 assert(listeners._nextListener == null); | 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 } |
| 524 _FutureListener listener = listeners; | 450 _FutureListener listener = listeners; |
| 525 | |
| 526 bool hasError = source._hasError; | |
| 527 // Do the actual propagation. | 451 // Do the actual propagation. |
| 528 // Set initial state of listenerHasError and listenerValueOrError. These | 452 // Set initial state of listenerHasValue and listenerValueOrError. These |
| 529 // variables are updated, with the outcome of potential callbacks. | 453 // variables are updated, with the outcome of potential callbacks. |
| 530 bool listenerHasError = hasError; | 454 bool listenerHasValue = true; |
| 531 final sourceValue = hasError ? source._error : source._value; | 455 final sourceValue = hasError ? null : source._value; |
| 532 var listenerValueOrError = sourceValue; | 456 var listenerValueOrError = sourceValue; |
| 533 // Set to true if a whenComplete needs to wait for a future. | 457 // Set to true if a whenComplete needs to wait for a future. |
| 534 // The whenComplete action will resume the propagation by itself. | 458 // The whenComplete action will resume the propagation by itself. |
| 535 bool isPropagationAborted = false; | 459 bool isPropagationAborted = false; |
| 536 // TODO(floitsch): mark the listener as pending completion. Currently | 460 // TODO(floitsch): mark the listener as pending completion. Currently |
| 537 // we can't do this, since the markPendingCompletion verifies that | 461 // we can't do this, since the markPendingCompletion verifies that |
| 538 // the future is not already marked (or chained). | 462 // the future is not already marked (or chained). |
| 539 // Only if we either have an error or callbacks, go into this, somewhat | 463 // Only if we either have an error or callbacks, go into this, somewhat |
| 540 // expensive, branch. Here we'll enter/leave the zone. Many futures | 464 // expensive, branch. Here we'll enter/leave the zone. Many futures |
| 541 // doesn't have callbacks, so this is a significant optimization. | 465 // doesn't have callbacks, so this is a significant optimization. |
| 542 if (hasError || (listener.handlesValue || listener.handlesComplete)) { | 466 if (hasError || (listener.handlesValue || listener.handlesComplete)) { |
| 543 Zone zone = listener._zone; | 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 } |
| 544 | 492 |
| 545 void handleError() { | 493 void handleError() { |
| 546 assert(listener.handlesError); | |
| 547 AsyncError asyncError = source._error; | 494 AsyncError asyncError = source._error; |
| 548 bool matchesTest = true; | 495 bool matchesTest = true; |
| 549 if (listener.hasErrorTest) { | 496 if (listener.hasErrorTest) { |
| 550 _FutureErrorTest test = listener._errorTest; | 497 _FutureErrorTest test = listener._errorTest; |
| 551 try { | 498 try { |
| 552 matchesTest = zone.runUnary(test, asyncError.error); | 499 matchesTest = zone.runUnary(test, asyncError.error); |
| 553 } catch (e, s) { | 500 } catch (e, s) { |
| 554 listenerValueOrError = identical(asyncError.error, e) ? | 501 listenerValueOrError = identical(asyncError.error, e) ? |
| 555 asyncError : new AsyncError(e, s); | 502 asyncError : new AsyncError(e, s); |
| 556 listenerHasError = true; | 503 listenerHasValue = false; |
| 557 return; | 504 return; |
| 558 } | 505 } |
| 559 } | 506 } |
| 560 if (matchesTest) { | 507 Function errorCallback = listener._onError; |
| 561 Function errorCallback = listener._onError; | 508 if (matchesTest && errorCallback != null) { |
| 562 assert(errorCallback != null); | |
| 563 try { | 509 try { |
| 564 if (errorCallback is ZoneBinaryCallback) { | 510 if (errorCallback is ZoneBinaryCallback) { |
| 565 listenerValueOrError = zone.runBinary(errorCallback, | 511 listenerValueOrError = zone.runBinary(errorCallback, |
| 566 asyncError.error, | 512 asyncError.error, |
| 567 asyncError.stackTrace); | 513 asyncError.stackTrace); |
| 568 } else { | 514 } else { |
| 569 listenerValueOrError = zone.runUnary(errorCallback, | 515 listenerValueOrError = zone.runUnary(errorCallback, |
| 570 asyncError.error); | 516 asyncError.error); |
| 571 } | 517 } |
| 572 } catch (e, s) { | 518 } catch (e, s) { |
| 573 listenerValueOrError = identical(asyncError.error, e) ? | 519 listenerValueOrError = identical(asyncError.error, e) ? |
| 574 asyncError : new AsyncError(e, s); | 520 asyncError : new AsyncError(e, s); |
| 575 listenerHasError = true; | 521 listenerHasValue = false; |
| 576 return; | 522 return; |
| 577 } | 523 } |
| 578 listenerHasError = false; | 524 listenerHasValue = true; |
| 525 } else { |
| 526 // Copy over the error from the source. |
| 527 listenerValueOrError = asyncError; |
| 528 listenerHasValue = false; |
| 579 } | 529 } |
| 580 } | 530 } |
| 581 | 531 |
| 582 void handleWhenCompleteCallback() { | 532 void handleWhenCompleteCallback() { |
| 583 var completeResult; | 533 var completeResult; |
| 584 try { | 534 try { |
| 585 completeResult = zone.run(listener._whenCompleteAction); | 535 completeResult = zone.run(listener._whenCompleteAction); |
| 586 } catch (e, s) { | 536 } catch (e, s) { |
| 587 if (hasError && identical(source._error.error, e)) { | 537 if (hasError && identical(source._error.error, e)) { |
| 588 listenerValueOrError = source._error; | 538 listenerValueOrError = source._error; |
| 589 } else { | 539 } else { |
| 590 listenerValueOrError = new AsyncError(e, s); | 540 listenerValueOrError = new AsyncError(e, s); |
| 591 } | 541 } |
| 592 listenerHasError = true; | 542 listenerHasValue = false; |
| 593 return; | 543 return; |
| 594 } | 544 } |
| 595 if (completeResult is Future) { | 545 if (completeResult is Future) { |
| 596 _Future result = listener.result; | 546 _Future result = listener.result; |
| 597 result._isChained = true; | 547 result._isChained = true; |
| 598 isPropagationAborted = true; | 548 isPropagationAborted = true; |
| 599 final _Future originalSource = source; | |
| 600 completeResult.then((ignored) { | 549 completeResult.then((ignored) { |
| 601 _propagateToFutures(originalSource, | 550 _propagateToListeners(source, new _FutureListener.chain(result)); |
| 602 new _FutureListener.chain(result), | |
| 603 _UNGUARDED); | |
| 604 }, onError: (error, [stackTrace]) { | 551 }, onError: (error, [stackTrace]) { |
| 605 // When there is an error, we have to make the error the new | 552 // When there is an error, we have to make the error the new |
| 606 // result of the current listener. | 553 // result of the current listener. |
| 607 if (completeResult is! _Future) { | 554 if (completeResult is! _Future) { |
| 608 // This should be a rare case. | 555 // This should be a rare case. |
| 609 completeResult = new _Future(); | 556 completeResult = new _Future(); |
| 610 completeResult._setError(error, stackTrace); | 557 completeResult._setError(error, stackTrace); |
| 611 } | 558 } |
| 612 _propagateToFutures(completeResult, | 559 _propagateToListeners(completeResult, |
| 613 new _FutureListener.chain(result), | 560 new _FutureListener.chain(result)); |
| 614 _UNGUARDED); | |
| 615 }); | 561 }); |
| 616 } | 562 } |
| 617 } | 563 } |
| 618 | 564 |
| 619 Zone oldZone = Zone._enter(zone); | |
| 620 | |
| 621 if (!hasError) { | 565 if (!hasError) { |
| 622 if (listener.handlesValue) { | 566 if (listener.handlesValue) { |
| 623 // Run try/catch in separate function to help compilers. | 567 listenerHasValue = handleValueCallback(); |
| 624 () { | |
| 625 try { | |
| 626 listenerValueOrError = zone.runUnary(listener._onValue, | |
| 627 sourceValue); | |
| 628 listenerHasError = false; | |
| 629 } catch (e, s) { | |
| 630 listenerValueOrError = new AsyncError(e, s); | |
| 631 listenerHasError = true; | |
| 632 } | |
| 633 }(); | |
| 634 assert(!listener.handlesComplete); | |
| 635 } else if (listener.handlesComplete) { | |
| 636 // Complete-listeners are distinct from value/error handlers, | |
| 637 // so no need to check for a complete handler if it is an error | |
| 638 // handler. | |
| 639 handleWhenCompleteCallback(); | |
| 640 } | 568 } |
| 641 } else { | 569 } else { |
| 642 if (!source._zone.inSameErrorZone(zone)) { | 570 handleError(); |
| 643 // Don't cross zone boundaries with errors. | |
| 644 Zone._leave(oldZone); | |
| 645 _throwUncaughtError(source, guarded); | |
| 646 return; | |
| 647 } | |
| 648 if (listener.handlesError) { | |
| 649 handleError(); | |
| 650 assert(!listener.handlesComplete); | |
| 651 } else if (listener.handlesComplete) { | |
| 652 handleWhenCompleteCallback(); | |
| 653 } | |
| 654 } | 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); |
| 655 | 577 |
| 656 Zone._leave(oldZone); | 578 if (isPropagationAborted) return; |
| 657 | |
| 658 if (isPropagationAborted) { | |
| 659 // Don't use the value in listenerValueOrError - the whenComplete | |
| 660 // handler is handling that result. | |
| 661 if (_pendingFutureListeners == null) return; | |
| 662 source = null; | |
| 663 continue; | |
| 664 } | |
| 665 // If the listener's value is a future we need to chain it. Note that | 579 // If the listener's value is a future we need to chain it. Note that |
| 666 // this can only happen if there is a callback. Since 'is' checks | 580 // this can only happen if there is a callback. Since 'is' checks |
| 667 // can be expensive, we're trying to avoid it. | 581 // can be expensive, we're trying to avoid it. |
| 668 if (!listenerHasError && | 582 if (listenerHasValue && |
| 669 !identical(sourceValue, listenerValueOrError) && | 583 !identical(sourceValue, listenerValueOrError) && |
| 670 listenerValueOrError is Future) { | 584 listenerValueOrError is Future) { |
| 671 Future chainSource = listenerValueOrError; | 585 Future chainSource = listenerValueOrError; |
| 672 // Shortcut if the chain-source is already completed. Just continue | 586 // Shortcut if the chain-source is already completed. Just continue |
| 673 // the loop. | 587 // the loop. |
| 674 _Future result = listener.result; | 588 _Future result = listener.result; |
| 675 if (chainSource is _Future) { | 589 if (chainSource is _Future) { |
| 676 if (chainSource._isComplete) { | 590 if (chainSource._isComplete) { |
| 677 // Propagate the value (simulating a tail call). | 591 // propagate the value (simulating a tail call). |
| 678 result._isChained = true; | 592 result._isChained = true; |
| 679 source = chainSource; | 593 source = chainSource; |
| 680 listeners = new _FutureListener.chain(result); | 594 listeners = new _FutureListener.chain(result); |
| 681 continue; | 595 continue; |
| 682 } else { | 596 } else { |
| 683 _chainCoreFuture(chainSource, result); | 597 _chainCoreFuture(chainSource, result); |
| 684 } | 598 } |
| 685 } else { | 599 } else { |
| 686 _chainForeignFuture(chainSource, result); | 600 _chainForeignFuture(chainSource, result); |
| 687 } | 601 } |
| 688 if (_pendingFutureListeners == null) return; | 602 return; |
| 689 source = null; | |
| 690 continue; | |
| 691 } | 603 } |
| 692 } | 604 } |
| 693 // Complete the future waiting for the result. | |
| 694 _Future result = listener.result; | 605 _Future result = listener.result; |
| 695 _FutureListener resultListeners = result._removeListeners(); | 606 listeners = result._removeListeners(); |
| 696 if (!listenerHasError) { | 607 if (listenerHasValue) { |
| 697 result._setValue(listenerValueOrError); | 608 result._setValue(listenerValueOrError); |
| 698 } else { | 609 } else { |
| 699 AsyncError asyncError = listenerValueOrError; | 610 AsyncError asyncError = listenerValueOrError; |
| 700 result._setErrorObject(asyncError); | 611 result._setErrorObject(asyncError); |
| 701 } | 612 } |
| 702 // Prepare for next round. | 613 // Prepare for next round. |
| 703 source = result; | 614 source = result; |
| 704 listeners = resultListeners; | |
| 705 } | 615 } |
| 706 } | 616 } |
| 707 | 617 |
| 708 Future timeout(Duration timeLimit, {onTimeout()}) { | 618 Future timeout(Duration timeLimit, {onTimeout()}) { |
| 709 if (_isComplete) return new _Future.immediate(this); | 619 if (_isComplete) return new _Future.immediate(this); |
| 710 _Future result = new _Future(); | 620 _Future result = new _Future(); |
| 711 Timer timer; | 621 Timer timer; |
| 712 if (onTimeout == null) { | 622 if (onTimeout == null) { |
| 713 timer = new Timer(timeLimit, () { | 623 timer = new Timer(timeLimit, () { |
| 714 result._completeError(new TimeoutException("Future not completed", | 624 result._completeError(new TimeoutException("Future not completed", |
| (...skipping 17 matching lines...) Expand all Loading... |
| 732 } | 642 } |
| 733 }, onError: (e, s) { | 643 }, onError: (e, s) { |
| 734 if (timer.isActive) { | 644 if (timer.isActive) { |
| 735 timer.cancel(); | 645 timer.cancel(); |
| 736 result._completeError(e, s); | 646 result._completeError(e, s); |
| 737 } | 647 } |
| 738 }); | 648 }); |
| 739 return result; | 649 return result; |
| 740 } | 650 } |
| 741 } | 651 } |
| 742 | |
| 743 class _PendingFutureListeners { | |
| 744 final _Future source; | |
| 745 final _PendingFutureListeners next; | |
| 746 _FutureListener listeners; | |
| 747 _PendingFutureListeners(this.source, this.listeners, this.next); | |
| 748 } | |
| OLD | NEW |