Hi, I am Sanjeet Tiwari...
Let's talk about

Back to Notes

Generators

JavaScript

Generators are special kind of functions which yields a set of values and returns an iterator which can traverse through the set of values via next() function.

Functions who have got * after function keyword are generator functions.

function* simpleGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

Let’s take the above generator function as an example. What if we do -

const c = simpleGenerator();

console.log(c.next());

The console.log will print out an object -

{ value: 1, done: false }

What if we do the same console.log(c.next()) again. We’ll see -

{ value: 2, done: false }

One more time, we’ll get -

{ value: 3, done: false }

and now, once all the values have been yielded, if we do a console.log(c.next()) again, we’ll get -

{ value: undefined, done: true }

As you can see, generator functions have the ability to pause their execution till next yield. And it returns a done flag which keeps the user informed if all the values have been yielded or not.

Multiple Generator Objects

Let’s say we create multiple generator objects by calling generator function multiple times -

const c1 = simpleGenerator();
const c2 = simpleGenerator();

A new generator function call will generate a new fresh instance of the iterator which can be used to get all the yielded values again.

So, if we have a code like -

// first run
console.log(c1.next())
console.log(c2.next())

// second run
console.log(c1.next())
console.log(c2.next())

We’ll get this in the console -

{ value: 1, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 2, done: false }

Once, a generator instance has been exhausted, it can’t be used again.

Return values of yield

Whenever a value is yielded inside a generator function, it also returns a value back which is nothing but whatever we passed to the next() function.

One more important rule to consider - value passed to the first next() function, won’t make any difference.

In the first iteration, yield will always return null.

console.log(c1.next(23432)) // won't matter
console.log(c1.next(2)) // will be returned
console.log(c1.next(3)) // will be returned

return() function

return() function is used to exit a generator function prematurely. A value can also be passed to return which will just set the returned value to be that passed value.

So, if I do console.log(c1.return(3000)), it will output -

{ value: 3000, done: true }

throw() function

As name suggests, it is used to just throw any Error in between.

c1.throw(new Error("Something went wrong"));

Applications of Generators

Infinity loop

As we all know, we can’t have infinity loops in our code, it will force our application to crash. But, what if we have a function which can infinitely generate new values ?

As an example, a function who will return a unique id every time can be achieved via Generators -

function* generateID(prefix = "id-") {
    let i = 0;
    while(true) {
        yield `${prefix}${i++}`;
    }
}

const idGenerator = generateID();

Now, every time we call idGenerator.next(), a new ID will be generated, and this will always return a unique value.

If we want to again start the id counter from 0, we can create a new instance of generateID generator function.

Creating Iterators

An iterator is a special entity in JavaScript which has got a next(), return() and a throw() function, precisely what we get after calling the generator function.

Generators can be used to convert normal arrays into Iterators -

function* arrToIterator(arr = []) {
    for (let i = 0; i < arr.length; i++) {
        yield arr[i];
    }
}

const i1 = arrToIterator([1, 3, 5]);
console.log(i1.next()); // will print { value: 1, done: false }
console.log(i1.next()); // will print { value: 3, done: false }

Video Sources

Learn JavaScript Generators In 12 Minutes

Learn JavaScript Generators In 12 Minutes

Web Dev Simplified

Last updated on 10-08-2024