| 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; |
| 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 |
| 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 try { |
| 445 listener._zone.run(() { | 439 if (!hasError) { |
| 446 // TODO(floitsch): mark the listener as pending completion. Currently | 440 var value = source._value; |
| 447 // we can't do this, since the markPendingCompletion verifies that | 441 if (listener._onValue != null) { |
| 448 // the future is not already marked (or chained). | 442 listenerValueOrError = zone.runUnary(listener._onValue, value); |
| 449 try { | 443 listenerHasValue = true; |
| 450 if (!hasError) { | |
| 451 var value = source._value; | |
| 452 if (listener._onValue != null) { | |
| 453 listenerValueOrError = listener._onValue(value); | |
| 454 listenerHasValue = true; | |
| 455 } else { | |
| 456 // Copy over the value from the source. | |
| 457 listenerValueOrError = value; | |
| 458 listenerHasValue = true; | |
| 459 } | |
| 460 } else { | 444 } else { |
| 461 _AsyncError asyncError = source._error; | 445 // Copy over the value from the source. |
| 462 _FutureErrorTest test = listener._errorTest; | 446 listenerValueOrError = value; |
| 463 bool matchesTest = true; | 447 listenerHasValue = true; |
| 464 if (test != null) { | |
| 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 } | 448 } |
| 449 } else { |
| 450 _AsyncError asyncError = source._error; |
| 451 _FutureErrorTest test = listener._errorTest; |
| 452 bool matchesTest = true; |
| 453 if (test != null) { |
| 454 matchesTest = zone.runUnary(test, asyncError.error); |
| 455 } |
| 456 if (matchesTest && listener._onError != null) { |
| 457 Function errorCallback = listener._onError; |
| 458 // TODO(ajohnsen): Use either runUnary or runBinary. |
| 459 listenerValueOrError = zone.run( |
| 460 () => _invokeErrorHandler(errorCallback, |
| 461 asyncError.error, |
| 462 asyncError.stackTrace)); |
| 463 listenerHasValue = true; |
| 464 } else { |
| 465 // Copy over the error from the source. |
| 466 listenerValueOrError = asyncError; |
| 467 listenerHasValue = false; |
| 468 } |
| 469 } |
| 479 | 470 |
| 480 if (listener._whenCompleteAction != null) { | 471 if (listener._whenCompleteAction != null) { |
| 481 var completeResult = listener._whenCompleteAction(); | 472 var completeResult = zone.run(listener._whenCompleteAction); |
| 482 if (completeResult is Future) { | 473 if (completeResult is Future) { |
| 483 listener._isChained = true; | 474 listener._isChained = true; |
| 484 completeResult.then((ignored) { | 475 completeResult.then((ignored) { |
| 485 // Try again, but this time don't run the whenComplete callback. | 476 // Try again, but this time don't run the whenComplete callback. |
| 486 _propagateToListeners(source, listener); | 477 _propagateToListeners(source, listener); |
| 487 }, onError: (error, [stackTrace]) { | 478 }, onError: (error, [stackTrace]) { |
| 488 // When there is an error, we have to make the error the new | 479 // When there is an error, we have to make the error the new |
| 489 // result of the current listener. | 480 // result of the current listener. |
| 490 if (completeResult is! _Future) { | 481 if (completeResult is! _Future) { |
| 491 // This should be a rare case. | 482 // This should be a rare case. |
| 492 completeResult = new _Future(); | 483 completeResult = new _Future(); |
| 493 completeResult._setError(error, stackTrace); | 484 completeResult._setError(error, stackTrace); |
| 494 } | 485 } |
| 495 _propagateToListeners(completeResult, listener); | 486 _propagateToListeners(completeResult, listener); |
| 496 }); | 487 }); |
| 497 isPropagationAborted = true; | 488 isPropagationAborted = true; |
| 498 } | |
| 499 } | 489 } |
| 500 } 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)) { | |
| 504 listenerValueOrError = source._error; | |
| 505 } else { | |
| 506 listenerValueOrError = new _AsyncError(e, s); | |
| 507 } | |
| 508 listenerHasValue = false; | |
| 509 } | 490 } |
| 510 }); | 491 } catch (e, s) { |
| 492 // Set the exception as error unless the error is the same as the |
| 493 // original one. |
| 494 if (hasError && identical(source._error.error, e)) { |
| 495 listenerValueOrError = source._error; |
| 496 } else { |
| 497 listenerValueOrError = new _AsyncError(e, s); |
| 498 } |
| 499 listenerHasValue = false; |
| 500 } |
| 501 // If we changed zone, old will not be null. |
| 502 if (old != null) Zone._leave(old); |
| 511 if (isPropagationAborted) return; | 503 if (isPropagationAborted) return; |
| 512 // If the listener's value is a future we need to chain it. | 504 // If the listener's value is a future we need to chain it. |
| 513 if (listenerHasValue && listenerValueOrError is Future) { | 505 if (listenerHasValue && listenerValueOrError is Future) { |
| 514 Future chainSource = listenerValueOrError; | 506 Future chainSource = listenerValueOrError; |
| 515 // Shortcut if the chain-source is already completed. Just continue the | 507 // Shortcut if the chain-source is already completed. Just continue the |
| 516 // loop. | 508 // loop. |
| 517 if (chainSource is _Future && chainSource._isComplete) { | 509 if (chainSource is _Future && chainSource._isComplete) { |
| 518 // propagate the value (simulating a tail call). | 510 // propagate the value (simulating a tail call). |
| 519 listener._isChained = true; | 511 listener._isChained = true; |
| 520 source = chainSource; | 512 source = chainSource; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 565 } | 557 } |
| 566 }, onError: (e, s) { | 558 }, onError: (e, s) { |
| 567 if (timer.isActive) { | 559 if (timer.isActive) { |
| 568 timer.cancel(); | 560 timer.cancel(); |
| 569 result._completeError(e, s); | 561 result._completeError(e, s); |
| 570 } | 562 } |
| 571 }); | 563 }); |
| 572 return result; | 564 return result; |
| 573 } | 565 } |
| 574 } | 566 } |
| OLD | NEW |