Dart Async Programming

By pjain      Published May 1, 2020, 2:15 a.m. in blog Programming   

Part of Dart Series

sync

async

Generator Functions

Generators 101

Generator functions produce sequence of values(in contrast to regular functions that return single value). Within the generator function, yield is a keyword that ‘returns’ single value to the sequence, but does not stop the generator function.

They are similar to streaming and boost performance both in computation but especially in space. This is because they generate values on demand - meaning, values get generated when someone tries to iterate over the iterator, or starts to listen to the stream, not before.

Generator functions can be: 1. Asynchronous (return a Stream of values) 2. Synchronous (return an Iterable with values)

The * marked eg sync* are keywords used in generator functions.

sync* iterator

yield example

void main() {
  Iterable<int> numbers = getNumbers(3); //sets up iterator/generator - but doesn't call it!
  print('starting to generate numbers ...');
  for (int val in numbers) { // each invocation gets 1 generated value - note 1st one actually inits func!
    print('$val');
  }
}
Iterable<int> getNumbers(int number) sync* { //NOTE sync* means invoker WAITs for each step
  print('generator started'); // Not invoked till first fetch of data 
  for (int i = 0; i < number; i++) {
    yield i;
  }
  print('generator ended');
}
/* // Outputs following
starting to generate numbers ...
generator started
0
1
2
generator ended
*/

iterator sync yield example

Yield* keyword is used for ‘returning’ recursive generator instead of normal yield one.

void main() {
  print('create iterator');
  Iterable<int> numbers = getNumbersRecursive(3);
  print('starting to iterate...');
  for (int val in numbers) {
    print('$val');
  }
  print('end of main');
}
Iterable<int> getNumbersRecursive(int number) sync* {
  print('generator $number started');
if (number > 0) {
    yield* getNumbersRecursive(number - 1);
  }
  yield number;
print('generator $number ended');
}
/* // Outputs following
create iterator
starting to iterate...
generator 3 started
generator 2 started
generator 1 started
generator 0 started
0
generator 0 ended
1
generator 1 ended
2
generator 2 ended
3
generator 3 ended
end of main*/

async* iterator - consumer listens for result

  • Here we are now streaming the list of numbers - so async listening
void main() {
  Stream<int> numbers = getNumbers(3); //sets up iterator/generator - but doesn't call it!
  print('starting to generate numbers ...Listening .. ');
  numbers.listen((int value) {
    print('$value');
  });
  print('Done generate numbers.');
}
Stream<int> getNumbers(int number) async* { //NOTE async* means invoker listens for each step
  print('generator started'); // Not invoked till first fetch of data 
  await new Future.delayed(new Duration(seconds: 5)); //sleep 5s
  print('started generating values...');
  for (int i = 0; i < number; i++) {
    await new Future.delayed(new Duration(seconds: 1)); //sleep 1s
    yield i;
  }
  print('ended generating values...');
}

/* // Outputs following - note now 0,1,2 are output with delays of seconds - async io
starting to generate numbers ...Listening .. 
Done generate numbers.
generator started
started generating values...
0
1
2
ended generating values...
*/

yield and yield*

Instead of generating a long list (could be millions of entries), you can iterate over each value, where the iterator yields up one value at a time, then caller processes it. So it puts space requirements from O(n) to O(1).

Resources


0 comments

There are no comments yet

Add new comment

Similar posts

List++ Advanced Application Features

Interview Questions in Flutter

NAV: URL Launcher, Deep Links

NAV: Navigation Drawers