So, how would we define a generator, compared to a normal function? You declare a generator function by using the * ( asterisk ) operator after the function keyword:
To pause the execution inside the generator, we use the statement yield:
yield 'Hey friend' // We pause the execution here
console.log('Back again') // When we resume, we are here
A generator gives you a next() method, which is used to start/resume the execution. This method returns an object with two keys:
value: [ yielded value ],
done: [ true if we reach the end]
Let's see a very simple generator in action:
At our first groceries.next() call, our groceriesGenerator reached our first yield statement and paused, returning the value Eggs to the caller. Our second next() resumed the execution at the second yield statement, pausing it again and returning Tomatoes...
Our last next() terminates the generator, returns Paper Bag and sets done to true.
In the introduction, I said that generators could help to implement iterators. Let's see an example:
In this example, you can see how the state of the generator is maintained accross invocations. Whne we resume the execution by calling next(), the variables and loops are the same.
Pass values back to generators
Finally, you can also pass a value back to a generator. Here is a example:
Here, our first next() will not print anything because we pause the execution before the first console.log. The second next() is called with an argument. This argument is provided as the returned value of the yield statement. We store this value inside the symbol variable and use it next.
You can also force a generator to throw an error: