In Dart, the async* and yield keywords are used together to create asynchronous generators, which allow you to produce a sequence of asynchronous values — essentially, to create a Stream easily without manually managing a StreamController.
async*marks a function as one that will emit multiple values asynchronously, returning aStream.yieldis used inside that function to emit values one by one over time.
Here’s a simple example:
Stream numberStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1)); // simulate delay
yield i; // emit one value at a time
}
}
void main() async {
await for (var number in numberStream()) {
print('Received: $number');
}
}
What happens here:
- The function
numberStream()is declared withasync*, so it returns aStream<int>. - Each time the loop runs, it waits for one second (because of the
await) and then yields one value to the stream. - The
await forloop inmain()listens to this stream and prints each value as it arrives.
This is different from a normal async function, which can only return one Future value.
An async* function, on the other hand, can yield many values over time — just like a continuous data source.
I’ve applied this concept in a Flutter project where I needed to emit real-time timer values for a countdown feature. Instead of using a StreamController, I used async* with yield, which made the code cleaner and easier to maintain.
For example:
Stream countdown(int start) async* {
for (int i = start; i >= 0; i--) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
