What is the Generator Function in JavaScript?

Learn via video course
FREE
View all courses
JavaScript Course With Certification: Unlocking the Power of JavaScript
JavaScript Course With Certification: Unlocking the Power of JavaScript
by Mrinal Bhattacharya
1000
4.8
Start Learning
JavaScript Course With Certification: Unlocking the Power of JavaScript
JavaScript Course With Certification: Unlocking the Power of JavaScript
by Mrinal Bhattacharya
1000
4.8
Start Learning
Topics Covered

What is the Generator Function in JavaScript?

Generator functions in Javascript are special functions that can generate a sequence of values. Generator Functions in Javascript are used to generate value. Whenever called, they return a Generator Object. The generator object returned by the generator function follows the Iterable Protocol of ES6, so it works similarly to iterators.

Calling the next() method on the generator object only executes the function till the first yield statement and the yield value is returned to the caller. When we repeatedly call the next() method, we can access a sequence of the objects containing two properties; one is thevalue, the value associated with the yield statement, and the other is a boolean flag done, to indicate whether there is something remaining in the function to execute or not.

The yield keyword is used inside the generator to pause and resume the execution. Further, the state of function is retained so that execution can be resumed from the last yield statement.
The normal functions in Javascript execute according to the non-preemptive or run to completion model, which means their execution cannot be paused in between, but generator functions have the capability to pause the execution in between with the help of the yield keyword.

Syntax

We create the generator function in Javascript by writing an asterisk after the function keyword.

We can also create an anonymous generator function in Javascript by omitting the name of the function.

Generator-Object

The generator object is a specific kind of object which is returned by the generator function. It follows the Iterable protocols of ECMAScript 2015.

Methods of Generator Object

1. next()

According to iterable protocols, generator object consists of a next() method. It returns the value of yield expression. The next() function when called, returns an IteratorResult object which consists of two properties.

  • value: To represent the actual value of the current object where the iterator is pointing.
  • done: To represent boolean information regarding whether some elements remain in the iterator or not.

2. return()

It returns the value as well as terminates the execution of the generator, the further call to the next function will always return {value:undefined, done:true}, which indicates there is nothing left to be executed in the generator function.

3. throw() It terminates the generator, followed by an error throw.

Status of Generator Object

1. suspended When the generator object is created but halt on execution.

2. closed When the generator is terminated, there could be three possibilities.

  • The generator finished all yield statements by iterating through successfully.
  • The return statement is being encountered or the return() method is called as by the generator object.
  • The throw() method is called in case of error occurs.

Examples

Simple Example

We aim to create a function that can maintain its state and provide us numbers based on auto-increment on subsequent calls to the next() method.

Output:

Explanation:

  • We have created a generator function in which we are executing a while loop. We have a variable num whose state will be maintained during the execution of the generator.
  • After the definition, we have called the generator function to get our generator object.
  • The generator object is being used to call the next() method so that we can receive our required numbers on each call.
  • The value: denotes the number and done: false denotes that few numbers are left in this generator object.
  • The value will become undefined and done will become true when there will be nothing left in the generator object to iterate on.

Passing Arguments into Generators

We can provide parameters to generators in a similar way as we do with the normal functions,

Output:

Explanation:

  • This example is similar to the last one, but here we have the concept of start parameter point as well.
  • The count of the number will start from the start variable instead of 1.
  • You can see two generator objects that are working in isolation according to the provided parameters.

Return Statement in a Generator

Whenever the generator function contains a return statement, the next() method can only execute the code written before that and only the yield statement written before the return statement will be executed, as soon as all yield statements execute and the next() encounters a return statement, the done boolean property is set to true. A further call to the next method will not execute the code of the generator but always return the value as undefined and done as true.

Output:

Explanation:

  • In this generator function we have a return statement.
  • The first call to the next() method through the generator object will execute the code till the first yield statement.
  • The next call will resume execution from just after the last yield statement.
  • Later we have a return statement that will return the final value and hence the done will be set to true.
  • The code that is written after the return statement will no longer be accessible, and if we continue to call the next() method, the same object: {value: undefined, done: true} will always be returned, signifying that there is nothing more in the iterator.

yield and yield* Syntax

The yield* expression is used to delegate the execution to another generator or iterable object. This yield* keyword iterates over the provided operand, and then it yield each value of that iterable.

Let's see some examples to understand this concept better.

Example 1: yield* with Iterable Object

If we have another iterable and need to iterate on that iterable before proceeding in the execution of the current generator function, we can use yield* with the iterable object as an operand; let's keep our focus on yield* so without creating an iterable, we will use a built in iterable, which are string array, Map, etc.

Output:

Explanation:

  • Here also we have defined a generator function, but as the traditional one, we are not only using yield keyword. See, there is yield* keyword after the first yield keyword.
  • The yield* will delegate the execution to the operand iterable, and you can notice that's the reason when we further call next() method it will no longer execute the current generator function.
  • The code written after yield* will wait until the iterable which is the operand of yield* is finished.

Example 2: Call a Generator from Another Generator

The same thing that we have seen in the previous example can be done using the generator instead of iterable because under the hood, the generator object is actually an iterable. The working will remain similar as it was earlier in the case of iterables, but this concept could be more formally termed as calling a generator function from another generator function.

The yield* keyword will pause the execution of the current generator function, and the control will be shifted towards the called generator function, the one whose function call would have been written after the yield* keyword.

Output:

Explanation:

  • We have called the anotherGeneratorFunction() with the yield* keyword, so it will be executed till the end before proceeding to the execution of generatorFunction().

Another Method to Create Iterable

By now you must have known that the generator function constructs an iterable object, when called. Let's now explore another conventional method of iterable implementation.

Output:

Explanation:

  • In the beginning, we created an empty object.
  • Later, we use Symbol.iterator, which is used to specify the iterator function for the object.
  • So now, whenever this object has iterated, the function associated with Symbol.iterator provides the value to the next function.
  • As we can see in the for of loop, the numbers from 1 to 6 have been printed because of the iterable object.

How to Throw an Exception from Generator Object

We have already seen that generator object consists of a method named throw() which we can use to throw an exception.

Output:

Explanation:

  • The flow of code stops once the throw() method is encountered by the interpreter.
  • To prevent the break of flow, we can use the try catch blocks in the generator function or more preferably wrap the throw() method inside the try block.

Using async/await Keywords With Generators

Just like we create an async function, we can create async generators by writing the async keyword before the function keyword.

Quick Recall: async and await are the keywords used to write the asynchronous code in a synchronous manner where the await keyword waits for the promise to be resolved or rejected and the async keyword is used to wrap all await keywords with a function.

Basic Example

Output: After each 5 Seconds.

Explanation:

  • Notice the value generated by the yield statement; this is currently a resolved promise.
  • The promise will be fulfilled after 5 seconds because we are using await inside the async IIFE, we are only getting results after the promise is fulfilled.
  • Outside the generator function, to wait for the promise to be resolved before print, we can use await keyword and store the result in a temporary variable.
  • In this way, we can write async generators.

API Call From Generator Function

This is very similar to the last example, the only difference is here, we are calling a API, and as soon as it provides with a generator function

Output:

Explanation:

  • First of all we have created an async generator function that accepts the URL as a parameter.
  • Then later on the fetch function is used to fetch the response from the URL provided as a parameter.
  • This https://jsonplaceholder.typicode.com/ is the server endpoint for a few APIs that are generally used for testing purposes, and in this article, we have used https://jsonplaceholder.typicode.com/users which will return an array of 10 users for GET request.
  • Here we are using await keyword to wait until the promise of fetch is resolved.
  • later, we are providing the response.json() to yield the statement, which will return the JSON response.
  • Outside the generator function we have an async IIFE to call the next() method with the generator object.
  • response.json() also returns a promise; that's why we are using the await keyword.
  • In the end you can see we are getting 10 users for the GET request on the dummy API endpoint.

Advantages of Generators

  • Memory Efficient, Because they work on running only when need model, means the value will be evaluated on each next() method call.
  • Lazy Evaluation, This technique improves the performance by just evaluating the expression whenever needed without evaluating the entire required data.

Limitation of Generators

  • We cannot define the generator function with the yield keyword inside the callback function.
  • If the generator object is providing some sequence of data and we want to access any element, then we cannot have random access.
  • Generator objects once come into a closed state then cannot be used again. We need to create a new generator object to iterate over once again.

Browser compatibility

This is supported by every major browser, except Internet Explorer.

BrowserVersion
Edge13
Firefox26
Internet ExplorerNot Supported
Opera26
Safari10
Chrome39
NodeJS4.0.0

Conclusion

  • Generator function in javascript is used to generate values. It returns a generator object.
  • These functions are written with function* syntax.
  • The yield keyword is used within the generator is used to pause and resume the execution of code.
  • The generator object follows the iterable protocols of ES6 hence it implements the next() function.
  • Whenever the next function is called with the generator object, it executes the code till the yield statement and the value written along with the yield keyword are returned to the caller.
  • After the partial execution, the generator function in Javascript saves its state and subsequently, whenever the next() function is called again, the execution resumes from the last state and proceeds till the next occurring yield statement in the function. This is one of the major features of the generator function that it can maintain a state.
  • yield* keyword delegates the execution of the generator function to another iterable, by using this we can call the generator function from another generator function.