Problem Statement
How do async* generators work in Dart? Provide examples of when to use async* and yield.
Explanation
Async* functions return Stream and use yield to emit values asynchronously over time, creating streams declaratively. Each yield pauses execution, emits a value, and resumes when the listener requests the next value, making async* perfect for creating custom streams without StreamController boilerplate. The function body executes lazily when someone subscribes to the stream.
Example: Stream<int> countDown(int from) async* { for (int i = from; i >= 0; i--) { await Future.delayed(Duration(seconds: 1)); yield i; } } creates a countdown stream emitting values every second. Use async* for transforming existing streams, creating sequences of async values, implementing pagination where each yield fetches the next page, or generating values based on async operations.
Yield* incorporates all values from another stream into your stream, useful for chaining or composing streams. For example, yield* otherStream; emits all values from otherStream as part of your stream. This enables building complex streams by combining simpler ones, creating powerful stream transformation and composition patterns.
Async* generators are lazy - code only runs when subscribed, and pauses between yields when not actively consumed. This makes them efficient for large or infinite sequences. Use async* when you need to create streams programmatically with complex logic, transform streams with custom operations, or implement async iteration patterns. It's more elegant than manually managing StreamController for many scenarios, providing cleaner syntax while maintaining full stream capabilities.
