Generators in JavaScript – Introduction

Introduction to generators

A generator function is a special kind of function that was introduced in ES2015 (ES6). In JavaScript once we start a function it has to run to its completion. However, generator functions enable us to create functions that another code can reenter multiple times. Furthermore, nothing from outside of the generation function can make it exit/pause. Generator function pauses itself when it runs into a yield expression. Once a execution reaches yield expression, generator can not continue execution on its own. Something from outside has to continue its execution.

Another important distinction from normal functions is that generator functions can produce multiple values during its execution. Hence, they can generate a sequence of values, not all at once, but on a per request basis. At every request generator function gives us a value until it reaches end of its execution. Once that happens done flag will be set to true.

When we call generator function it returns an object – generator. That object conforms to both Iterator protocol and Iterable protocol.

Compiler treats a function as generator function if it uses the function* keyword and contains at least one yield expression.

As we saw in previous post, iterator is an object that knows how to access items of a collection one at a time. Iterator provides a next() method. Next() returns the next item in the collection along with a flag done to tell us if we have finished iterating.

 

Show me the code part

As we can see from the code when we call generator function for the first time it returns us iterator-like object. Calling next() for the first time starts generator function and it yields first value. Furthermore, calling it for second time it gets us second and last value. Finally, third and last call returns no value but done flag is set to true this time meaning that we have finished iterating through generator.

We certainly do not have to do this “manually” and repeat iterator.next() call in our code  We can definitely use some kind of loop to achieve the same results:

 

Since generator function returns us generator (iterator protocol) we can use built-in for..of expression to “loop through generator”:

 

Sending values in and out

Lets see how we can communicate with generator function.

First we call generator function print() and assign the value to generator variable. To start execution of generator function we call next() method on generator. It is important to notice that first time we call  generator.next() we do not pass in any value because anything passed to next() during first call compiler ignores. Only usage of first call of next() is to get print() method executing. 

First line of print() method gets executed: let a = 5 - (yield 3); and once a compiler hits yield keyword execution of print() gets paused.

Execution continues at line 8: let iteratorResult = iterator.next();  . Variable iteratorResult gets a value of object: {value: 3, done: false}

To continue execution of print() function we have to call generator’s next() method:

iteratorResult = generator.next(1);

Execution continues at line 2: let a = 5 - (yield 3); and variable a is now 4. Since there are no more yield expressions in function print()  execution of the generator function ends at line 3.

Code continues at line 11: iteratorResult = generator.next(1); and we print a value for iterator result which is an object: {value: undefined, done: true}

 

Key takeaways

Some key takeaways from this post:

  • Generator function returns a generator – iterator object. Then we can use it to work with generator function
  • It is possible to “pause” and “continue” generator functions
  • We can “pause” generator function only from inside – function itself
  • yield keyword indicates where the function should pause
  • We can “start” and “continue” their execution only from outside
  • Generator functions can produce multiple values during its execution
  • They produce values on a per request basis
  • Compiler treats a function as generator if it uses function* keyword and contains at least one yield expression

Also published on Medium.

Ibrahim Šuta

Software Engineer, Mentor and Consultant interested and specialising in ASP.NET Core, C#, JavaScript, Angular, React.js. Experienced in MeteorJS, Node.js, ASP.NET, Windows Forms and Web Forms as well.