Chromium Code Reviews| 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); |
| (...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 404 return; | 404 return; |
| 405 } | 405 } |
| 406 if (listeners == null) return; | 406 if (listeners == null) return; |
| 407 _Future listener = listeners; | 407 _Future listener = listeners; |
| 408 if (listener._nextListener != null) { | 408 if (listener._nextListener != null) { |
| 409 // Usually futures only have one listener. If they have several, we | 409 // Usually futures only have one listener. If they have several, we |
| 410 // handle them specially. | 410 // handle them specially. |
| 411 _propagateMultipleListeners(source, listeners); | 411 _propagateMultipleListeners(source, listeners); |
| 412 return; | 412 return; |
| 413 } | 413 } |
| 414 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { | 414 Zone zone = listener._zone; |
| 415 if (hasError && !source._zone.inSameErrorZone(zone)) { | |
| 415 // Don't cross zone boundaries with errors. | 416 // Don't cross zone boundaries with errors. |
| 416 _AsyncError asyncError = source._error; | 417 _AsyncError asyncError = source._error; |
| 417 source._zone.handleUncaughtError( | 418 source._zone.handleUncaughtError( |
| 418 asyncError.error, asyncError.stackTrace); | 419 asyncError.error, asyncError.stackTrace); |
| 419 return; | 420 return; |
| 420 } | 421 } |
| 421 if (!identical(Zone.current, listener._zone)) { | 422 Zone old; |
|
floitsch
2014/01/29 13:35:46
Nit: I would prefer "oldZone", but "old" doesn't s
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 422 // Run the propagation in the listener's zone to avoid | 423 if (!identical(Zone.current, zone)) { |
| 423 // zone transitions. The idea is that many chained futures will | 424 // Change zone if it's not current. |
| 424 // be in the same zone. | 425 old = Zone._enter(zone); |
| 425 listener._zone.run(() { | |
| 426 _propagateToListeners(source, listener); | |
| 427 }); | |
| 428 return; | |
| 429 } | 426 } |
| 430 | |
| 431 // Do the actual propagation. | 427 // Do the actual propagation. |
| 432 // TODO(floitsch): Do we need to go through the zone even if we | |
| 433 // don't have a callback to execute? | |
| 434 bool listenerHasValue; | 428 bool listenerHasValue; |
| 435 var listenerValueOrError; | 429 var listenerValueOrError; |
| 436 // Set to true if a whenComplete needs to wait for a future. | 430 // Set to true if a whenComplete needs to wait for a future. |
| 437 // The whenComplete action will resume the propagation by itself. | 431 // The whenComplete action will resume the propagation by itself. |
| 438 bool isPropagationAborted = false; | 432 bool isPropagationAborted = false; |
| 439 // Even though we are already in the right zone (due to the optimization | 433 // At this point we are in the right zone. Each callback is invoked |
|
floitsch
2014/01/29 13:35:46
You can just remove those two lines. I'm not sure
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 440 // above), we still need to go through the zone. The overhead of | 434 // through Zone.run* to be sure to invoke potential callbacks. |
| 441 // executeCallback is however smaller when it is already in the correct | 435 // TODO(floitsch): mark the listener as pending completion. Currently |
| 442 // zone. | 436 // we can't do this, since the markPendingCompletion verifies that |
| 443 // TODO(floitsch): only run callbacks in the zone, not the whole | 437 // the future is not already marked (or chained). |
| 444 // handling code. | 438 |
| 445 listener._zone.run(() { | 439 bool handleValueCallback() { |
| 446 // TODO(floitsch): mark the listener as pending completion. Currently | |
| 447 // we can't do this, since the markPendingCompletion verifies that | |
| 448 // the future is not already marked (or chained). | |
| 449 try { | 440 try { |
| 450 if (!hasError) { | 441 listenerValueOrError = zone.runUnary(listener._onValue, |
| 451 var value = source._value; | 442 source._value); |
| 452 if (listener._onValue != null) { | 443 return true; |
| 453 listenerValueOrError = listener._onValue(value); | 444 } catch (e, s) { |
| 454 listenerHasValue = true; | 445 listenerValueOrError = new _AsyncError(e, s); |
| 446 return false; | |
| 447 } | |
| 448 } | |
| 449 | |
| 450 bool handleError() { | |
| 451 _AsyncError asyncError = source._error; | |
| 452 _FutureErrorTest test = listener._errorTest; | |
| 453 bool matchesTest = true; | |
| 454 if (test != null) { | |
| 455 try { | |
| 456 matchesTest = zone.runUnary(test, asyncError.error); | |
| 457 } catch (e, s) { | |
| 458 listenerValueOrError = identical(asyncError.error, e) ? | |
|
floitsch
2014/01/29 13:35:46
I wonder if we shouldn't change the semantics here
Anders Johnsen
2014/01/29 15:30:33
Adding TODO to be sure we revisit this.
| |
| 459 asyncError : new _AsyncError(e, s); | |
| 460 listenerHasValue = false; | |
| 461 return false; | |
| 462 } | |
| 463 } | |
| 464 Function errorCallback = listener._onError; | |
| 465 if (matchesTest && errorCallback != null) { | |
| 466 try { | |
| 467 if (errorCallback is ZoneBinaryCallback) { | |
| 468 listenerValueOrError = zone.runBinary(errorCallback, | |
| 469 asyncError.error, | |
| 470 asyncError.stackTrace); | |
| 455 } else { | 471 } else { |
| 456 // Copy over the value from the source. | 472 listenerValueOrError = zone.runUnary(errorCallback, |
| 457 listenerValueOrError = value; | 473 asyncError.error); |
| 458 listenerHasValue = true; | |
| 459 } | 474 } |
| 460 } else { | 475 } catch (e, s) { |
| 461 _AsyncError asyncError = source._error; | 476 listenerValueOrError = identical(asyncError.error, e) ? |
| 462 _FutureErrorTest test = listener._errorTest; | 477 asyncError : new _AsyncError(e, s); |
| 463 bool matchesTest = true; | 478 listenerHasValue = false; |
| 464 if (test != null) { | 479 return false; |
| 465 matchesTest = test(asyncError.error); | |
| 466 } | |
| 467 if (matchesTest && listener._onError != null) { | |
| 468 Function errorCallback = listener._onError; | |
| 469 listenerValueOrError = _invokeErrorHandler(errorCallback, | |
| 470 asyncError.error, | |
| 471 asyncError.stackTrace); | |
| 472 listenerHasValue = true; | |
| 473 } else { | |
| 474 // Copy over the error from the source. | |
| 475 listenerValueOrError = asyncError; | |
| 476 listenerHasValue = false; | |
| 477 } | |
| 478 } | 480 } |
| 481 listenerHasValue = true; | |
| 482 } else { | |
| 483 // Copy over the error from the source. | |
| 484 listenerValueOrError = asyncError; | |
| 485 listenerHasValue = false; | |
| 486 } | |
| 487 return true; | |
| 488 } | |
| 479 | 489 |
| 480 if (listener._whenCompleteAction != null) { | 490 void handleWhenCompleteCallback() { |
| 481 var completeResult = listener._whenCompleteAction(); | 491 var completeResult; |
| 482 if (completeResult is Future) { | 492 try { |
| 483 listener._isChained = true; | 493 completeResult = zone.run(listener._whenCompleteAction); |
| 484 completeResult.then((ignored) { | |
| 485 // Try again, but this time don't run the whenComplete callback. | |
| 486 _propagateToListeners(source, listener); | |
| 487 }, onError: (error, [stackTrace]) { | |
| 488 // When there is an error, we have to make the error the new | |
| 489 // result of the current listener. | |
| 490 if (completeResult is! _Future) { | |
| 491 // This should be a rare case. | |
| 492 completeResult = new _Future(); | |
| 493 completeResult._setError(error, stackTrace); | |
| 494 } | |
| 495 _propagateToListeners(completeResult, listener); | |
| 496 }); | |
| 497 isPropagationAborted = true; | |
| 498 } | |
| 499 } | |
| 500 } catch (e, s) { | 494 } catch (e, s) { |
| 501 // Set the exception as error unless the error is the same as the | |
| 502 // original one. | |
| 503 if (hasError && identical(source._error.error, e)) { | 495 if (hasError && identical(source._error.error, e)) { |
| 504 listenerValueOrError = source._error; | 496 listenerValueOrError = source._error; |
| 505 } else { | 497 } else { |
| 506 listenerValueOrError = new _AsyncError(e, s); | 498 listenerValueOrError = new _AsyncError(e, s); |
| 507 } | 499 } |
| 508 listenerHasValue = false; | 500 listenerHasValue = false; |
| 509 } | 501 } |
| 510 }); | 502 if (completeResult is Future) { |
| 503 listener._isChained = true; | |
| 504 completeResult.then((ignored) { | |
| 505 // Try again, but this time don't run the whenComplete callback. | |
|
floitsch
2014/01/29 13:35:46
// Try again. Since the future is marked as chaine
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 506 _propagateToListeners(source, listener); | |
| 507 }, onError: (error, [stackTrace]) { | |
| 508 // When there is an error, we have to make the error the new | |
| 509 // result of the current listener. | |
| 510 if (completeResult is! _Future) { | |
| 511 // This should be a rare case. | |
| 512 completeResult = new _Future(); | |
| 513 completeResult._setError(error, stackTrace); | |
| 514 } | |
| 515 _propagateToListeners(completeResult, listener); | |
| 516 }); | |
| 517 isPropagationAborted = true; | |
|
floitsch
2014/01/29 13:35:46
move to before the completeResult.then?
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 518 } | |
| 519 } | |
| 520 | |
| 521 if (!hasError) { | |
| 522 if (listener._onValue != null) { | |
| 523 listenerHasValue = handleValueCallback(); | |
| 524 } else { | |
| 525 listenerValueOrError = source._value; | |
| 526 listenerHasValue = true; | |
| 527 } | |
| 528 if (listenerHasValue && listener._whenCompleteAction != null) { | |
|
floitsch
2014/01/29 13:35:46
I don't think you need this.
The _whenCompleteActi
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 529 handleWhenCompleteCallback(); | |
|
floitsch
2014/01/29 13:35:46
It would be easier to optimize, if handleWhenCompl
Anders Johnsen
2014/01/29 15:30:33
Done.
| |
| 530 } | |
| 531 } else { | |
| 532 if (handleError() && listener._whenCompleteAction != null) { | |
| 533 handleWhenCompleteCallback(); | |
| 534 } | |
| 535 } | |
| 536 // If we changed zone, old will not be null. | |
| 537 if (old != null) Zone._leave(old); | |
| 511 if (isPropagationAborted) return; | 538 if (isPropagationAborted) return; |
| 512 // If the listener's value is a future we need to chain it. | 539 // If the listener's value is a future we need to chain it. |
| 513 if (listenerHasValue && listenerValueOrError is Future) { | 540 if (listenerHasValue && listenerValueOrError is Future) { |
| 514 Future chainSource = listenerValueOrError; | 541 Future chainSource = listenerValueOrError; |
| 515 // Shortcut if the chain-source is already completed. Just continue the | 542 // Shortcut if the chain-source is already completed. Just continue the |
| 516 // loop. | 543 // loop. |
| 517 if (chainSource is _Future && chainSource._isComplete) { | 544 if (chainSource is _Future && chainSource._isComplete) { |
| 518 // propagate the value (simulating a tail call). | 545 // propagate the value (simulating a tail call). |
| 519 listener._isChained = true; | 546 listener._isChained = true; |
| 520 source = chainSource; | 547 source = chainSource; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 565 } | 592 } |
| 566 }, onError: (e, s) { | 593 }, onError: (e, s) { |
| 567 if (timer.isActive) { | 594 if (timer.isActive) { |
| 568 timer.cancel(); | 595 timer.cancel(); |
| 569 result._completeError(e, s); | 596 result._completeError(e, s); |
| 570 } | 597 } |
| 571 }); | 598 }); |
| 572 return result; | 599 return result; |
| 573 } | 600 } |
| 574 } | 601 } |
| OLD | NEW |