You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Update spec for yield\*.
Was very under-specified.
This change changes the responsiveness of a `yield*` to be that
of `StreamController.addStream`, meaning that it *immediately*
reacts to pause and cancel and forwards it to the inner stream.
Unlike `await for`, a `yield*` is at the yield for the entire
duration of the inner stream, and can cancel at any time, not only
when control reaches a `yield` inside the loop.
Experience has taught us that with `async*` functions, `await for`
and `yield*`, back-pressure using `cancel` or `pause` *must*
be acted on immediately, to avoid a stream computation continuing
when it has nothing meaningful to do, and the caller knows that
and tries to stop it.
This text likely specifies *more* behavior than any current implementation does.
The most important part is to wait for events to be delivered, pausing if necessary, forwarding pause/cancel immediately even between events, and to wait for the `cancel` future if cancelled.
Checking for being cancelled before listening, instead of listeneing and immediately cancelling, is less important, but it's imperative to pause or cancel right after listening if the outer stream is paused or cancelled, because there may never come a first event.
For example, if you do `stream.first`, it should cancel after
the first event, before starting on the computation of the next
event, because then it's too late to cancel. Canonical example:
```dart
// Stream with one event and no done event.
Stream<int> oneValue => Stream<int>.multi((c) => c.add(1));
Stream<int> clone1(Stream<int> stream) async* {
await for (var event in stream) yield event;
}
Stream<int> clone2(Stream<int> stream) async* {
yield* stream;
}
void main() async {
print(await oneValue.first); // 1
print(await clone1(oneValue).first);
print(await clone2(oneValue).first);
print(await clone2(clone1(oneValue)).first);
}
```
If the `first` code's cancel doesn't reach the `clone1` before
it goes back to the `await for` loop, the cancel will never
be processed because control never reaches a `yield` again.
0 commit comments