by Tiago Lopes Ferreira
Let’s explore ES6 Generators
Generators are an implementation of iterables.
The big deal about generators is that they are functions that can suspend its execution while maintaining the context.
This behaviour is crucial when dealing with executions that need to be paused, but its context maintained in order to recover it in the future.
Does async development sounds familiar here?
The syntax for generators starts with it’s
function* declaration (please note the asterisk) and the
yield through which a generator can pause it’s execution.
generator function creates new generator that we can use to control the process through
next will execute our
generator’s code until an
yield expression is reached.
At this point the value on
yield is emitted and the
generator’s execution is suspended.
yield was born with generators and allow us to emit values. However, we can only do this while we are inside a generator.
If we try to
yield a value on a callback, for instance, even if declared inside the generator, we will get an error.
yield* was built to enable calling a generator within another generator.
b iterator, produced by
bar generator, does not work as expected when calling
This is because, although the execution of
foo produces an iterator, we do not iterate over it.
That’s why ES6 brought the operator
This works perfectly with data consumers.
yield* goes over every element on the generator and
Generators as Iterators
Generators are simple iterables, which means that they follow the
iterableprotocol says that an object should return a function iterator whose key is
iteratorprotocol says that the iterator should be an object pointing to the next element of the iteration. This object should contain a function called
Because generators are iterables then we can use a data consumer, e.g.
for-of, to iterate over generators’ values.
We can add a
return statement to our generator, however
return will behave differently according to the way generators’ data is iterated.
When performing the iteration by hand, using
next, we get our returned value (i.e.
done) as the last
value of our iterator object and our
done flag as true.
On the side, when using a defined data consumer such as
destructuring, the returned value is ignored.
We saw that
yield* allows us to call a generator inside a generator.
It also allow us to store the value returned by the executed generator.
throw inside a generator and
next will propagate our exception.
As soon as an exception is thrown the iterator flow breaks and it’s state is set to
done: true indefinitely.
Generators as Data Consumers
Besides generators being data producers, through
yield, they also have the ability to consume data using
There’s some interesting points to explore here.
Generator Creation (1)
At this stage we are creating our generator
Our execution stops at point
First next (2)
The first execution of
next gets our generator to be executed until the first
On this first execution any value sent through
next is ignored. This is because there’s no
yield statement until the first
yield statement 👹
Our execution suspends at
B waiting for a value to be filled to
Next next (3)
On the next executions of
next our generator will run the code until the next
In our case, it logs the value that is got through
Got: foo) and it gets suspended again on
Because generators are an iterable implementation, when created we get an iterable object, where each
yield represents the value to emitted on each iteration. This description allow us to use generators to create iterables.
The following example represents a generator as iterable that iterates over even numbers until
max is reached. Because our generator returns an iterable we can use
for-of to iterate over the values.
It’s useful to remember that
yield pauses the generator’s execution, and on each iteration the generator resumes from where it was paused.
We can use generators to better work with async code, such as
This use case it a good introduction to the new
async/await on ES8.
Using co library and a generator our code will look more like synchronous code.
As for the new
async/await our code will look a lot like our previous version.
Generators are an implementation of iterables and follow the
iterator protocol. Therefore they can be used to build iterables.
The most amazing thing about generators is their ability to suspend their execution. For this ES6 brings a new statement called
However, calling a generator inside a generator is not as easy as executing the generator function. For that, ES6 has
Generators are the next step to bring asynchronous development close to synchronous.
Thanks to 🍻
- Axel Rauschmayer for his Exploring ES6 — Generators
- Nicolás Bevacqua for his PonyFoo — ES6 Generators in Depth
- Jake Archibald for his promises example on developers.google.com
- To all Regular Show fans
Be sure to check out my other articles on ES6