Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library _js_helper; | 5 library _js_helper; |
| 6 | 6 |
| 7 import 'dart:_async_await_error_codes' as async_error_codes; | 7 import 'dart:_async_await_error_codes' as async_error_codes; |
| 8 | 8 |
| 9 import 'dart:_js_embedded_names' show | 9 import 'dart:_js_embedded_names' show |
| 10 DEFERRED_LIBRARY_URIS, | 10 DEFERRED_LIBRARY_URIS, |
| (...skipping 3696 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3707 /// If the async* function wants to do an await it calls this function with | 3707 /// If the async* function wants to do an await it calls this function with |
| 3708 /// [object] not and [IterationMarker]. | 3708 /// [object] not and [IterationMarker]. |
| 3709 /// | 3709 /// |
| 3710 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | 3710 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |
| 3711 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. | 3711 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. |
| 3712 void asyncStarHelper(dynamic object, | 3712 void asyncStarHelper(dynamic object, |
| 3713 dynamic /* int | js function */ bodyFunctionOrErrorCode, | 3713 dynamic /* int | js function */ bodyFunctionOrErrorCode, |
| 3714 AsyncStarStreamController controller) { | 3714 AsyncStarStreamController controller) { |
| 3715 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { | 3715 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
| 3716 // This happens on return from the async* function. | 3716 // This happens on return from the async* function. |
| 3717 if (controller.cancelationCompleter != null) { | 3717 if (controller.isCanceled) { |
| 3718 controller.cancelationCompleter.complete(); | 3718 controller.cancelationCompleter.complete(); |
| 3719 } else { | 3719 } else { |
| 3720 controller.close(); | 3720 controller.close(); |
| 3721 } | 3721 } |
| 3722 return; | 3722 return; |
| 3723 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { | 3723 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
| 3724 // The error is a js-error. | 3724 // The error is a js-error. |
| 3725 if (controller.cancelationCompleter != null) { | 3725 if (controller.isCanceled) { |
| 3726 controller.cancelationCompleter.completeError( | 3726 controller.cancelationCompleter.completeError( |
| 3727 unwrapException(object), | 3727 unwrapException(object), |
| 3728 getTraceFromException(object)); | 3728 getTraceFromException(object)); |
| 3729 } else { | 3729 } else { |
| 3730 controller.addError(unwrapException(object), | 3730 controller.addError(unwrapException(object), |
| 3731 getTraceFromException(object)); | 3731 getTraceFromException(object)); |
| 3732 controller.close(); | 3732 controller.close(); |
| 3733 } | 3733 } |
| 3734 return; | 3734 return; |
| 3735 } | 3735 } |
| 3736 | 3736 |
| 3737 if (object is IterationMarker) { | 3737 if (object is IterationMarker) { |
| 3738 if (controller.cancelationCompleter != null) { | 3738 if (controller.cancelationCompleter != null) { |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
if (controller.isCanceled) {
?
sigurdm
2015/04/10 08:13:24
Done.
| |
| 3739 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3739 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3740 async_error_codes.STREAM_WAS_CANCELED)(null); | 3740 async_error_codes.STREAM_WAS_CANCELED) |
| 3741 (null); | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
Odd linebreak.
Could (null) be on the previous lin
sigurdm
2015/04/10 08:13:25
It is to emphasize the call of the returned functi
| |
| 3741 return; | 3742 return; |
| 3742 } | 3743 } |
| 3743 if (object.state == IterationMarker.YIELD_SINGLE) { | 3744 if (object.state == IterationMarker.YIELD_SINGLE) { |
| 3744 controller.add(object.value); | 3745 controller.add(object.value); |
| 3745 // If the controller is paused we stop producing more values. | 3746 |
| 3746 if (controller.isPaused) { | |
| 3747 return; | |
| 3748 } | |
| 3749 // TODO(sigurdm): We should not suspend here according to the spec. | |
| 3750 scheduleMicrotask(() { | 3747 scheduleMicrotask(() { |
| 3748 if (controller.isPaused) { | |
| 3749 // We only suspend the thread inside the microtask in order to allow | |
| 3750 // listeners on the output stream to pause in response to the just | |
| 3751 // output value, and have the stream immediately stop producing. | |
| 3752 controller.isSuspended = true; | |
| 3753 return; | |
| 3754 } | |
| 3751 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3755 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3752 async_error_codes.SUCCESS) | 3756 async_error_codes.SUCCESS) |
| 3753 (null); | 3757 (null); |
| 3754 }); | 3758 }); |
| 3755 return; | 3759 return; |
| 3756 } else if (object.state == IterationMarker.YIELD_STAR) { | 3760 } else if (object.state == IterationMarker.YIELD_STAR) { |
| 3757 Stream stream = object.value; | 3761 Stream stream = object.value; |
| 3758 controller.isAdding = true; | |
| 3759 // Errors of [stream] are passed though to the main stream. (see | 3762 // Errors of [stream] are passed though to the main stream. (see |
| 3760 // [AsyncStreamController.addStream]. | 3763 // [AsyncStreamController.addStream]. |
| 3761 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. | 3764 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
| 3762 controller.addStream(stream).then((_) { | 3765 controller.addStream(stream).then((_) { |
| 3763 controller.isAdding = false; | 3766 // We do not check for isPaused here because the spec 17.16.2 only |
| 3767 // demands checks for each element in [stream] not after the last one. | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
... demands checks *before* each element ...
sigurdm
2015/04/10 08:13:25
Done.
| |
| 3764 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3768 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3765 async_error_codes.SUCCESS)(null); | 3769 async_error_codes.SUCCESS) |
| 3770 (null); | |
| 3766 }); | 3771 }); |
| 3767 return; | 3772 return; |
| 3768 } | 3773 } |
| 3769 } | 3774 } |
| 3770 | 3775 |
| 3771 Future future = object is Future ? object : new Future.value(object); | 3776 Future future = object is Future ? object : new Future.value(object); |
| 3772 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3777 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3773 async_error_codes.SUCCESS), | 3778 async_error_codes.SUCCESS), |
| 3774 onError: (error, StackTrace stackTrace) { | 3779 onError: (error, StackTrace stackTrace) { |
| 3775 ExceptionAndStackTrace wrapped = | 3780 ExceptionAndStackTrace wrapped = |
| 3776 new ExceptionAndStackTrace(error, stackTrace); | 3781 new ExceptionAndStackTrace(error, stackTrace); |
| 3777 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3782 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3778 async_error_codes.ERROR) | 3783 async_error_codes.ERROR) |
| 3779 (wrapped); | 3784 (wrapped); |
| 3780 }); | 3785 }); |
| 3781 } | 3786 } |
| 3782 | 3787 |
| 3783 Stream streamOfController(AsyncStarStreamController controller) { | 3788 Stream streamOfController(AsyncStarStreamController controller) { |
| 3784 return controller.stream; | 3789 return controller.stream; |
| 3785 } | 3790 } |
| 3786 | 3791 |
| 3787 /// A wrapper around a [StreamController] that remembers if that controller | 3792 /// A wrapper around a [StreamController] that keeps track of the state of |
| 3788 /// got a cancel. | 3793 /// the execution of an async* function. |
| 3794 /// It can be in 1 of 3 states: | |
| 3789 /// | 3795 /// |
| 3790 /// Also has a subSubscription that when not null will provide events for the | 3796 /// - running/scheduled |
| 3791 /// stream, and will be paused and resumed along with this controller. | 3797 /// - suspended |
| 3798 /// - canceled | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
I guess the "done" state is implicit.
sigurdm
2015/04/10 08:13:25
Yes - it corresponds to controller.isClosed
| |
| 3799 /// | |
| 3800 /// If yielding while the subscription is paused it will become suspended. And | |
| 3801 /// only resume after the subscription is resumed or canceled. | |
| 3792 class AsyncStarStreamController { | 3802 class AsyncStarStreamController { |
| 3793 StreamController controller; | 3803 StreamController controller; |
| 3794 Stream get stream => controller.stream; | 3804 Stream get stream => controller.stream; |
| 3805 | |
| 3806 /// True when the async* function has yielded while being paused. | |
| 3807 /// Or if it has yielded a | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
incomplete sentence.
sigurdm
2015/04/10 08:13:25
Thanks
| |
| 3808 /// When true execution will only resume after a `onResume` or `onCancel` | |
| 3809 /// event. | |
| 3810 bool isSuspended = false; | |
| 3811 | |
| 3812 bool get isPaused => controller.isPaused; | |
| 3813 | |
| 3795 Completer cancelationCompleter = null; | 3814 Completer cancelationCompleter = null; |
| 3815 | |
| 3816 /// True after the StreamSubscription has been cancelled. | |
| 3817 /// When this is true, errors thrown from the async* body should go to the | |
| 3818 /// [cancelationCompleter] instead of adding them to [controller], and | |
| 3819 /// returning from the async function should complete [cancelationCompleter]. | |
| 3796 bool get isCanceled => cancelationCompleter != null; | 3820 bool get isCanceled => cancelationCompleter != null; |
| 3797 bool isAdding = false; | 3821 |
| 3798 bool isPaused = false; | |
| 3799 add(event) => controller.add(event); | 3822 add(event) => controller.add(event); |
| 3823 | |
| 3800 addStream(Stream stream) { | 3824 addStream(Stream stream) { |
| 3801 return controller.addStream(stream, cancelOnError: false); | 3825 return controller.addStream(stream, cancelOnError: false); |
| 3802 } | 3826 } |
| 3827 | |
| 3803 addError(error, stackTrace) => controller.addError(error, stackTrace); | 3828 addError(error, stackTrace) => controller.addError(error, stackTrace); |
| 3829 | |
| 3804 close() => controller.close(); | 3830 close() => controller.close(); |
| 3805 | 3831 |
| 3806 AsyncStarStreamController(body) { | 3832 AsyncStarStreamController(body) { |
| 3833 | |
| 3834 _resumeBody() { | |
| 3835 Function wrapped = | |
| 3836 _wrapJsFunctionForAsync(body, async_error_codes.SUCCESS); | |
| 3837 wrapped(null); | |
| 3838 } | |
| 3839 | |
| 3807 controller = new StreamController( | 3840 controller = new StreamController( |
| 3808 onListen: () { | 3841 onListen: () { |
| 3809 scheduleMicrotask(() { | 3842 scheduleMicrotask(_resumeBody); |
| 3810 Function wrapped = _wrapJsFunctionForAsync(body, | |
| 3811 async_error_codes.SUCCESS); | |
| 3812 wrapped(null); | |
| 3813 }); | |
| 3814 }, | |
| 3815 onPause: () { | |
| 3816 isPaused = true; | |
| 3817 }, onResume: () { | 3843 }, onResume: () { |
| 3818 isPaused = false; | 3844 // Only schedule again if the async* function actually os suspended. |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
os -> is
sigurdm
2015/04/10 08:13:25
Done.
| |
| 3819 if (!isAdding) { | 3845 // Resume directly instead of scheduling, so that the sequence |
| 3820 asyncStarHelper(null, body, this); | 3846 // `pause-resume-pause` will result in one extra event produced. |
| 3847 if (isSuspended) { | |
| 3848 isSuspended = false; | |
| 3849 _resumeBody(); | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
I think you still need to do a scheduleMicrotask (
sigurdm
2015/04/10 08:13:25
Ah - now I understand what you meant. I thought it
| |
| 3821 } | 3850 } |
| 3822 }, onCancel: () { | 3851 }, onCancel: () { |
| 3852 // If the async* is finished we ignore cancel events. | |
| 3823 if (!controller.isClosed) { | 3853 if (!controller.isClosed) { |
| 3824 cancelationCompleter = new Completer(); | 3854 cancelationCompleter = new Completer(); |
| 3825 if (isPaused) asyncStarHelper(null, body, this); | 3855 if (isSuspended) { |
| 3826 | 3856 // Resume the suspended async* function to run finalizers. |
| 3857 isSuspended = false; | |
| 3858 scheduleMicrotask(_resumeBody); | |
|
Lasse Reichstein Nielsen
2015/04/09 12:22:09
Shouldn't the body be called with async_error_code
sigurdm
2015/04/10 08:13:25
Good catch - Done
| |
| 3859 } | |
| 3827 return cancelationCompleter.future; | 3860 return cancelationCompleter.future; |
| 3828 } | 3861 } |
| 3829 }); | 3862 }); |
| 3830 } | 3863 } |
| 3831 } | 3864 } |
| 3832 | 3865 |
| 3833 makeAsyncStarController(body) { | 3866 makeAsyncStarController(body) { |
| 3834 return new AsyncStarStreamController(body); | 3867 return new AsyncStarStreamController(body); |
| 3835 } | 3868 } |
| 3836 | 3869 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3932 // This is a function that will return a helper function that does the | 3965 // This is a function that will return a helper function that does the |
| 3933 // iteration of the sync*. | 3966 // iteration of the sync*. |
| 3934 // | 3967 // |
| 3935 // Each invocation should give a body with fresh state. | 3968 // Each invocation should give a body with fresh state. |
| 3936 final dynamic /* js function */ _outerHelper; | 3969 final dynamic /* js function */ _outerHelper; |
| 3937 | 3970 |
| 3938 SyncStarIterable(this._outerHelper); | 3971 SyncStarIterable(this._outerHelper); |
| 3939 | 3972 |
| 3940 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | 3973 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
| 3941 } | 3974 } |
| OLD | NEW |