dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.2k stars 1.57k forks source link

We need yield* from sync to async #39015

Open britov opened 4 years ago

britov commented 4 years ago

Hello! I want yield all from sync generator to async generator. I ran into this in bloc pattern.

Is it possible to add the ability to do so?

Stream<int> generateNewState(ValueHolder<int> holder) async* {
  yield* _iterableGenerator();
}

Iterable<int> _iterableGenerator() sync* {}

I made a simple example of my problem. https://dartpad.dartlang.org/262582a392409037db83f4d8cd6f4c52

eernstg commented 4 years ago

Sounds like you could use Stream.fromIterable:

Stream<int> generateNewState(ValueHolder<int> holder) async* {
  yield* Stream.fromIterable(_iterableGenerator());
}

Iterable<int> _iterableGenerator() sync* {}
lrhn commented 4 years ago

That, or just:

for (var x in _iterableGenerator()) yield x;

The yield* is special in that it means different things in different contexts, but what we really need is the async behavior which can't be simulated by await for (var x in stream) yield x; because errors break the await for, but yield* forwards the errors too. The yield* in sync* functions is really just a convenience.

britov commented 4 years ago

Different behavior on mobile(flutter app) and web(dartPad). Console output in different sequence

void main() async {
  generateNewState().listen(print);
  await Future.delayed(Duration(seconds: 10));
}

Stream<int> generateNewState() async* {
  await for(var x in _generateStream()) {yield x;}
}

Stream<int> _generateStream() async* {
  print('before yield 1');
  yield 1;
  print('before yield 2');
  yield 2;
  print('before yield 3');
  yield 3;
}

If you use yield* the same behavior for mobile and web

Stream<int> generateNewState() async* {
  yield* _generateStream();
}
britov commented 4 years ago

We have several nested asynchronous generators, why yield andyield *work differently. yield * immediately pushes the new value into the parent (main) stream and yield 'asynchronously' pushes the new value

example: https://dartpad.dartlang.org/ede199b6df212d355a5cc21c8189883e

case with yield*

before yield 100 main() 100 before yield 200 main() 200

case with yield

before yield 300 before yield 400 main() 300 main() 400