Generator in JavaScript

generator function js

In ECMA2015 generators are introduced in javascript. Functions in Javascript run until return or end. Generators run until yield or return or end. Generator is a process that can be passed and resumed and can yield multiple values. It provides a new way of working with functions and iterators.

Using a generator:

  • You can stop the execution of a function from anywhere inside the function.
  • Continue executing from pause position.

Regular function return only one single value or nothing. It is executed upto completion. It cannot pause in middle and continue from pause position.

On the other hand generators can yield multiple values, one after another on demand. It works with iterators, to create a data stream with ease.

Create Generators:

To create a generator, we define a generator function with *. The objects of generator function are called generators.

Syntax:
// define a generator function
function* generator_function() {
   ... .. ...
}

// creating a generator
const generator_object = generator_function();

This generator function can also be defined in expressions

// Generator function expression
const generatorFunction = function*() {}
Using yield and next():

Generator function once called returns generator object, which holds generator iterable that can be iterated using next() method or for … of loop.

Every next() method executes every line of code until next yield it encounter and suspends its execution temporarily.

// generator function
function* generatorFunc() {

    console.log("1 invoke");
    yield 1;
    
   console.log("2 invoke");
    yield 2;
}

// returns generator object
const generator = generatorFunc();
console.log(generator.next());
Output:

1 invoke

{ value: 1, done: false }

Here a generator object is created. When generator.next() is called it executes upto yield 1 and pause the execution.

Using multiple yields:
// generator function
function* generatorFunc() {

    console.log("1 invoke");
    yield 1;
    
   console.log("2 invoke");
    yield 2;
   console.log("3 invoke");
}

// returns generator object
const generator = generatorFunc();

console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output:

1 invoke

{ value: 1, done: false }

2 invoke

{ value: 2, done: false }

3 invoke

{ value: undefined, done: true }

Here after first generator.next() executes 1 invoke upto yield 1 and pause.

Second generator.next() executes from pause position and executes 2 invoke upto yield 2 and pause again.

Third generator.next() executes from pause position and all yields are accessed so returns { value: undefined, done: true }

Using for… of loop:

Since generators are iterables we use for … of loop.

// generator function

function* generatorFunc() {

    console.log("1 invoke");

    yield 1;
console.log("2 invoke");

    yield 2;

}

// returns generator object

const generator = generatorFunc();

for ( const g of generator){

   console.log(g);

}
Output:

1 invoke

1

2 invoke

2

Generator methods:
MethodDescription
next()Returns a value of yield
return()Returns a value and terminates the generator
throw()Throws an error and terminates the generator
Generator states:

The next table lists the possible states of a Generator object:

StatusDescription
suspendedGenerator has halted execution but has not terminated.
closedGenerator has terminated by either encountering an error, returning, or iterating through all values.
Return vs yield keyword:
return keywordyield keyword
returns the value and terminates the functionreturns the value and pause the function
available in both normal and generator functionavailable only in generator function
Using return :
// generator function
function* generatorFunc() {

    yield 1;

   return 22;

    yield 3;
}
// returns generator object
const generator = generatorFunc();

console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
Output:

{ value: 1, done: false }

{ value: 22, done: true }

{ value: undefined, done: true }

We can use return() method instead of return keyword as generator.return()

Here when return statement is encountered it returns the value, done is set to true and terminates the function and return nothing after that.

Generator throw() method:
// 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: 100, done: false }

Error: Error occurred.