Summary: in this tutorial, you will learn about Dart Generators including synchronous and asynchronous generators.
Introduction to the Dart Generators
In Dart, generators are functions that produce a sequence of values. Unlike lists, generators generate a sequence of values on-demand without computing the entire sequence upfronts. Dart supports two types of generators:
- Synchronous generators
- Asynchronous generators
Generators are useful when dealing with large or infinite sequences of values because they avoid the need to compute and store the entire sequence upfront.
Synchronous generators
Synchronous generators are functions that generate values synchronously, once at a time, and pause their execution until the next value is requested.
To create a synchronous generator, you follow these steps:
- First, define a function that returns an Iterable<T>.
- Second, use the keyword
sync*
in the function header. - Third, use the
yield
keyword to generate a value of the sequence.
For example, the following defines a synchronous generator that generates a sequence of numbers from start
(inclusive) to end
(exclusive):
Iterable<int> range(int start, int end) sync* {
for (var i = start; i < end; i++) {
yield i;
}
}
void main() {
var numbers = range(1, 5);
for (var number in numbers) {
print(number);
}
}
Code language: Dart (dart)
Output:
1
2
3
4
Code language: plaintext (plaintext)
How it works:
First, defines a synchronous generator function called range
that takes two parameters: start
and end
. It returns an Iterable<int>
that represents a sequence of integers between start
(inclusive) and end
(exclusive).
The sync*
keyword indicates that the range
function is a synchronous generator.
Inside the range
function, we use a for
loop to iterate over the range of values from start
to end
.
In each iteration of the loop, we use the yield
keyword to produce the current value of i
. The yield
statement temporarily suspends the execution of the generator and provides the current value to the consumer of the generator.
After yielding a value, the generator resumes execution and continues to the next iteration of the loop.
Once the loop completes, the generator implicitly ends, and no further values are generated.
Second, define the main
function as the entry point of the program.
In the main
function, the range
function is called with arguments 1
and 5
, creating an Iterable<int>
named numbers
.
The numbers
iterable represents the sequence of numbers from 1 to 4 (as the end
value is exclusive).
We use a for-in
loop is used to iterate over each number in the numbers
iterable. In each iteration of the loop, the we assign current number to the variable number
and use print
function to output the value of number
.
The loop continues until all numbers in the numbers
iterable have been processed. Once the loop completes, the program execution ends.
Asynchronous generators
Asynchronous generators generate values asynchronously and allow for non-blocking operations. To create an asynchronous generator, you follow these steps:
- First, define a function that returns a Stream<T>.
- Second, use the keyword
async*
in the function header. - Third, use the
yield await
keywords to generate a value of the sequence.
For example, the following asynchronous generator generates a sequence of numbers from start
to end
every second:
import 'dart:async';
Stream<int> range(int start, int end) async* {
for (var i = start; i < end; i++) {
await Future.delayed(Duration(seconds: 1));
yield await i;
}
}
Future<void> main() async {
var stream = range(1, 5);
stream.listen(
(number) => print(number),
);
}
Code language: Dart (dart)
Output:
1
2
3
4
Code language: plaintext (plaintext)
How it works:
First, import the dart:async
library, which includes classes and utilities for asynchronous programming in Dart:
import 'dart:async';
Code language: JavaScript (javascript)
Second, define an asynchronous generator function called range
that takes two parameters: start
and end
. It returns a Stream<int>
that represents a stream of integers between start
(inclusive) and end
(exclusive):
Stream<int> range(int start, int end) async* {
for (var i = start; i < end; i++) {
await Future.delayed(Duration(seconds: 1));
yield await i;
}
}
Code language: JavaScript (javascript)
In this function, the async*
keyword indicates that the range
function is an asynchronous generator. It uses a for
loop is used to iterate over the range of values from start
to end
.
In each iteration of the loop, the await Future.delayed(Duration(seconds: 1))
statement introduces a delay of 1 second. The delay ensures that each value in the stream is produced asynchronously at a 1-second interval.
After the delay, the yield
keyword is used to produce the current value of i
. The await
keyword is used to wait for the value of i
.
The yield
statement temporarily suspends the execution of the generator and provides the current value to the consumer of the stream. After yielding a value, the generator resumes execution and continues to the next iteration of the loop.
Once the loop completes, the generator implicitly ends, and no further values are produced in the stream.
Third, define the main()
function which is the entry point of the program. In the main
function, we call the range
function with arguments 1
and 5
, creating a Stream<int>
named stream
.
The stream
represents the asynchronous stream of numbers from 1 to 4 (as the end
value is exclusive) emitted at 1-second intervals.
We call the listen
method on the stream
to register a listener that will handle the events emitted by the stream.
Inside the listen
method, we provide an anonymous function as the callback to handle each number emitted by the stream. The callback function uses the print
function to output the value of number
.
The listener continues to receive and process events emitted by the stream until the stream is closed.
Once all events have been processed and the stream is closed, the program execution ends.
Summary
- Use synchronous and asynchronous generators to generate sequences of values.
- Use generators to handle large or infinite sequences of values.