generators

Generators are simply subtypes of Iterators which I wrote about previously. They are a special kind of function that can be suspended and resumed, which is making a difference to iterators. Generators use function* and yield operators to work their magic.

The yield operator returns a value from the function and when the generator is resumed, execution continues after the yield.

We also have to use function* (with star character) instead of a function to return a generator instance.

!!! Generators have been borrowed from Python language.

The most magical feature in ES6!

Why? Take a look:

 1 function* generator () {
 2   yield 1;
 3   // pause
 4   yield 2;
 5   // pause
 6   yield 3;
 7   // pause
 8   yield 'done?';
 9   // done
10 }
11 let gen = generator(); // [object Generator]
12 
13 console.log(gen.next()); // Object {value: 1, done: false}
14 console.log(gen.next()); // Object {value: 2, done: false}
15 console.log(gen.next()); // Object {value: 3, done: false}
16 console.log(gen.next()); // Object {value: 'done?', done: false}
17 console.log(gen.next()); // Object {value: undefined, done: true}
18 console.log(gen.next()); // Object {value: undefined, done: true}
19 
20 for (let val of generator()) {
21   console.log(val); // 1
22                     // 2
23                     // 3
24                     // 'done?'
25 }

As you can see, the generator has four yield statements. Each returns a value, pauses execution and moves to the next yield when next() method is called. Calling a function produces an object for controlling generator execution, a so-called generator object.

Use is similar to iterators. We have next() method as I mentioned above and we can even use it with for-of loop.

Below is an example of a generator called random1_10, which returns random numbers from 1 to 10.

 1 function* random1_10 () {
 2   while (true) {
 3     yield Math.floor(Math.random() * 10) + 1;
 4   }
 5 }
 6 
 7 let rand = random1_10();
 8 console.log(rand.next());
 9 console.log(rand.next());
10 // …

Generator has never ending while loop. It produces random numbers every time when you call next() method.

ES5 implementation:

 1 function random1_10 () {
 2   return {
 3     next: function() {
 4       return {
 5         value: Math.floor(Math.random() * 10) + 1,
 6         done: false
 7       };
 8     }
 9   };
10 }
11 
12 let rand = random1_10();
13 console.log(rand.next());
14 console.log(rand.next());
15 // …

We can also mix generators together:

 1 function* random (max) {
 2   yield Math.floor(Math.random() * max) + 1;
 3 }
 4 
 5 function* random1_20 () {
 6   while (true) {
 7     yield* random(20);
 8   }
 9 }
10 
11 let rand = random1_20();
12 console.log(rand.next());
13 console.log(rand.next());
14 // …

random1_20 generator returns random values from 1 to 20. It uses random generator inside to create random number each time when yield statement is reached.