Generators in Dart

What are Generators?

Instead of building an array containing all the values and returning them all at once, a generator yields the values one at a time, which requires less memory and allows the caller to get started processing the first few values immediately

A function is a generator if its body is marked with the sync* or async* modifier.

Dart has built-in support for two kinds of generator functions:

  1. Synchronous generator: Returns an Iterable object.
  2. Asynchronous generator: Returns a Stream object.

Synchronous generator

To implement a synchronous generator function, mark the function body as sync*, and use yield statements to deliver values:

Synchronous generator returns an iterable.

// Synchronous generator: Returns an Iterable object.
Iterable<int> SequenceOfValues(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

void main() {
  print(SequenceOfValues(5)); //(0, 1, 2, 3, 4)
}

Asynchronous generator

To implement an asynchronous generator function, mark the function body as async*, and use yield statements to deliver values:

Asynchronous generator returns an stream.

// Asynchronous generator: Returns an Stream object.
Stream<int> SequenceOfValuesAsync(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

void main() async {
  var result = await SequenceOfValuesAsync(5).toSet(); //convert to set
  print(result); //{0, 1, 2, 3, 4}
}

Recursive generator

If your generator is recursive, you can improve its performance by using yield*:

Iterable<int> SequenceOfValues(int n) sync* {
  if (n > 0) {
    yield n;
    yield* SequenceOfValues(n - 1);
  }
}

void main() {
  var result = SequenceOfValues(5);
  print(result); // (5, 4, 3, 2, 1)
}