Dart Generators

 

Dart Generators

Dart Generator is a unique function that allows us to produce a sequence of values. Generators return values on demand; it means the value is generated when we try to iterate over the iterators. Dart provides built-in support for two types of generator functions.

  • Synchronous Generators
  • Asynchronous Generators

Synchronous Generator

It returns an iterable object which carries value synchronously. The yield keyword is used along with marking the synchronous generator function body as sync* to generator values.

Let's understand the following example of a synchronous generator.

Example -

  1. main()  {  
  2.     print("Dart Synchronous Generator Example.");  
  3.     oddNumber(10).forEach(print);   
  4. }  
  5.     // syn* functions returns an iterable  
  6.   
  7.    Iterable<int> oddNumber(int num) sync* {  
  8.     int k = num;  
  9.     while(k >= 0) {  
  10.          if(k%2 == 1) {  
  11.             // 'yield' statement  
  12.             yield k;  
  13.   
  14.                 }  
  15. k--;  
  16. }  
  17. }  

Output

Dart Synchronous Generator Example.
9
7
5
3
1

Explanation:

In the above program, we declared an oddNumber(10) function with the foreach loop without its body. The foreach loop will iterate the function. Now, we created oddNumber(10) function as synchronous generator function.

In the function body, we initialized a new variable k, which assigns the argument n. Then, we applied while loop to iterate the function, and the loop body is iterated until the value of k is less than or equal to 0.

We did modulus operation on k to find the odd numbers from the generators. We used the yield statement, which suspends the function's execution and returns the value at a time. It will return the value of each execution of the generator function. When the while loop's condition becomes false, then the loop terminated and prints the odd numbers.

Asynchronous Generators

It returns a stream object which carries value asynchronously. The yield keyword is used along with marking the asynchronous generator function body as async* to generator values.

Let's understand the following example -

Example -

  1. main()  {  
  2.     print("Dart Asynchronous Generator Example.");  
  3.     asyncNaturalsTo(10).forEach(print);   
  4. }  
  5.     // async* functions returns an stream object  
  6.   
  7.    Stream<int> asyncNaturalsTo(int num) async* {  
  8.     int k = 0;  
  9.     while(k < num) {  
  10.    
  11.             // 'yield' statement  
  12.             yield k++;  
  13.   
  14.                 }  
  15. k--;  
  16. }  

Output

Dart Asynchronous Generator Example.
0
1
2
3
4
5
6
7
8
9

Explanation:

The above code generated values asynchronously. The asyncNaturalsTo(int num) function returns a stream objects in each execution of function body. Here, the yield keyword behaved the same as the previous example; it stopped the execution of the function, returned the value, and resumed the execution for the next iteration. It will happen again and again until the function body is terminated.

Let's understand the following concepts related to the generator function.

The yield Keyword

The yield returns the single value to the sequence at a time but does not stop the execution of the generator function completely. It returns a value for each execution of the generator function.

The sync* Keyword -

The sync* keyword is used to declare the synchronize generator function. It returns the value when we try to iterator the value not when it was created. Let's have a look at the following example -

Example -

  1. void main() {  
  2.   print('creating iterator');  
  3.   Iterable<int> numbers = getNumbers(4);  // Here we are creating iterator  
  4.   print('Iteration starts...');  
  5.   for (int i in numbers) {  
  6.     print('$i');        // Iterate over the iterator  
  7.   }  
  8.   print('end of main function');  
  9. }  
  10. Iterable<int> getNumbers(int n) sync* {            // define generator synchronously  
  11.   print('generator started');  
  12.   for (int i = 0; i < n; i++) {  
  13.     yield i;  
  14.   }  
  15.   print('generator function ended');  
  16. }  

Output

creating iterator
Iteration starts...
generator started
0
1
2
3
generator function ended
end of main function

Explanation -

The above generator function generated the value when we iterate over the iterator.

The async* Keyword

The async keyword is used to declare the asynchronous generators. It returns the stream object. Let's understand the following example -

Example -

  1. void main() {  
  2.   print('creating iterator');  
  3.   Stream<int> numbers = getNumbers(4);  
  4.   print('starting to listen...');  
  5.   numbers.listen((int k) {  
  6.     print('$k');  
  7.   });  
  8.   print('end of the main function');  
  9. }  
  10. Stream<int> getNumbers(int number) async* {   // declaring asynchronous generator function  
  11.   print('waiting inside generator a 3 seconds :)');   
  12.   await new Future.delayed(new Duration(seconds: 3)); //sleep 3s  
  13.   print('started generating values...');  
  14.   for (int i = 0; i < number; i++) {  
  15.     await new Future.delayed(new Duration(seconds: 1)); //sleep 1s  
  16.     yield i;  
  17.   }  
  18.   print('ended generating values...');  
  19. }  

Output

creating iterator
starting to listen...
end of the main function
waiting inside generator a 3 seconds :)
started generating values...
0
1
2
3
ended generating values...

Note - Copy above code and paste it in your dart editor. You can clearly see the difference between the synchronous generators and asynchronous generators

Comments