In JavaScript, generators provide a new way to work with functions and iterators.
Using a generator,
- you can stop the execution of a function from anywhere inside the function
- and continue executing code from a halted position
Create JavaScript Generators
To create a generator, you need to first define a generator function with function*
symbol. The objects of generator functions are called generators.
// define a generator function
function* generator_function() {
... .. ...
}
// creating a generator
const generator_obj = generator_function();
Note: The generator function is denoted by *
. You can either use function* generatorFunc() {...}
or function *generatorFunc(){...}
to create them.
Using yield to Pause Execution
As mentioned above, you can pause the execution of a generator function without executing the whole function body. For that, we use the yield
keyword. For example,
// generator function
function* generatorFunc() {
console.log("1. code before the first yield");
yield 100;
console.log("2. code before the second yield");
yield 200;
}
// returns generator object
const generator = generatorFunc();
console.log(generator.next());
Output
1. code before the first yield {value: 100, done: false}
Here,
- A generator object named
generator
is created. - When
generator.next()
is called, the code up to the firstyield
is executed. Whenyield
is encountered, the program returns the value and pauses the generator function.
Note: You need to assign generator objects to a variable before you use it.
Working of multiple yield Statements
The yield
expression returns a value. However, unlike the return
statement, it doesn't terminate the program. That's why you can continue executing code from the last yielded position. For example,
function* generatorFunc() {
console.log("1. code before first yield");
yield 100;
console.log("2. code before the second yield");
yield 200;
console.log("3. code after the second yield");
}
const generator = generatorFunc();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output
1. code before first yield {value: 100, done: false} 2. code before second yield {value: 200, done: false} {value: undefined, done: true}
Here is how this program works.
- The first
generator.next()
statement executes the code up to the first yield statement and pauses the execution of the program. - The second
generator.next()
starts the program from the paused position. - When all the elements are accessed, it returns {value: undefined, done: true}.
Passing Arguments to Generator Functions
You can also pass arguments to a generator function. For example,
// generator function
function* generatorFunc() {
// returns 'hello' at first next()
let x = yield 'hello';
// returns passed argument on the second next()
console.log(x);
console.log('some code');
// returns 5 on second next()
yield 5;
}
const generator = generatorFunc();
console.log(generator.next());
console.log(generator.next(6));
console.log(generator.next());
Output
{value: "hello", done: false} 6 some code {value: 5, done: false} {value: undefined, done: true}
In the above program,
- The first
generator.next()
returns the value of theyield
(in this case, 'hello'). However, the value is not assigned to variable x inlet x = yield 'hello';
{value: "hello", done: false}
- When
generator.next(6)
is encountered, the code again starts atlet x = yield 'hello';
and the argument 6 is assigned to x. Also, remaining code is executed up to the secondyield
.6 some code {value: 5, done: false}
- When the third
next()
is executed, the program returns {value: undefined, done: true}. It is because there are no other yield statements.{value: undefined, done: true}
Generators are Used for Implementing Iterables
Generators provide an easier way to implement iterators.
If you want to implement an iterator manually, you have to create an iterator with the next()
method and save the state. For example,
// creating iterable object
const iterableObj = {
// iterator method
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step === 1) {
return { value: '1', done: false};
}
else if (step === 2) {
return { value: '2', done: false};
}
else if (step === 3) {
return { value: '3', done: false};
}
return { value: '', done: true };
}
}
}
}
for (const i of iterableObj) {
console.log(i);
}
Output
1 2 3
Since generators are iterables, you can implement an iterator in an easier way. Then you can iterate through the generators using the for...of
loop. For example,
// generator function
function* generatorFunc() {
yield 1;
yield 2;
yield 3;
}
const obj = generatorFunc();
// iteration through generator
for (let value of obj) {
console.log(value);
}
Generator Methods
Method | Description |
---|---|
next() |
Returns a value of yield |
return() |
Returns a value and terminates the generator |
throw() |
Throws an error and terminates the generator |
JavaScript return Vs yield Keyword
return Keyword | yield Keyword |
---|---|
Returns the value and terminates the function. | Returns the value and halts the function but does not terminate the function. |
Available in both the normal functions and generator functions. | Available only in generator functions. |
JavaScript Generator Function With return
You can use the return
statement in a generator function. The return
statement returns a value and terminates the function (similar to regular functions). For example,
// generator function
function* generatorFunc() {
yield 100;
return 123;
console.log("2. some code before second yield");
yield 200;
}
// returns generator object
const generator = generatorFunc();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output
{value: 100, done: false} {value: 123, done: true} {value: undefined, done: true}
In the above program, when the return
statement is encountered, it returns the value and done
property becomes true
, and the function terminates. Hence, the next()
method after the return
statement does not return anything.
Note: You can also use the return()
method instead of the return
statement like generator.return(123);
in the above code.
JavaScript Generator Throw Method
You can explicitly throw an error on the generator function using the throw() method. The use of throw()
method throws an error and terminates the function. For example,
// generator function
function* generatorFunc() {
yield 100;
yield 200;
}
// returns generator object
const generator = generatorFunc();
console.log(generator.next());
// throws an error
// terminates the generator
console.log(generator.throw(new Error('Error occurred.')));
console.log(generator.next());
Output
{value: 1, done: false} Error: Error occurred.
Uses of Generators
- Generators let us write cleaner code while writing asynchronous tasks.
- Generators provide an easier way to implement iterators.
- Generators execute its code only when required.
- Generators are memory efficient.
Generators were introduced in ES6. Some browsers may not support the use of generators. To learn more, visit JavaScript Generators support.