JavaScript Iterator and Generator [In-Depth Tutorial]


Written By - Olorunfemi Akinlua

Introduction to JavaScript Iterators and Generators

In JavaScript, an iterator is an object that defines a sequence and a return value for that sequence. It allows you to iterate over a collection of values, such as an array or an object, and perform a specific operation on each value. Iterators are a powerful feature of the language that can greatly simplify the process of working with collections of data.

A generator is a special type of function that can be used to create an iterator. It allows you to define a sequence of values and pause the execution of the function at any point, returning a value and saving the state of the function. This makes generators a useful tool for implementing iterators and creating sequences of data that can be iterated over.

In this article, we will explore the concepts of iterators and generators in JavaScript and discuss how they can be used in your code.

 

How Iterators work?

The for/of loop and spread operator work seamlessly with iterable objects, but it is worth understanding what is actually happening to make the iteration work. There are three separate types that you need to understand to understand iteration in JavaScript.

  • First, there are the iterable objects: these are types like Array, Set, and Map that can be iterated.
  • Second, there is the iterator object itself, which performs the iteration.
  • And third, there is the iteration result object that holds the result of each step of the iteration.

An iterator has two main methods: next() and return(). The next() method returns an object with two properties: value and done. The value property is the next value in the sequence, and the done property is a boolean that indicates if the iterator has reached the end of the sequence. When the iterator is finished, the done property will be true and the value property will be undefined.

Here is an example of a simple iterator that iterates over an array of numbers:

const numbers = [1, 2, 3, 4];

const numbersIterator = {
    index: 0,
    next: function () {
        if (this.index < numbers.length) {
            return { value: numbers[this.index++], done: false };
        } else {
            return { done: true };
        }
    },
};

To use this iterator, we can call the next() method repeatedly until the done property is true.

let result = numbersIterator.next();
while (!result.done) {
    console.log(result.value);
    result = numbersIterator.next();
}

Output

1
2
3
4

This will output the numbers 1 through 4 to the console.

 

How Generator works?

A generator is a special type of function that can be paused and resumed. When a generator function is called, it does not execute the function body immediately. Instead, it returns a generator object that can be used to execute the function body.

A generator function is defined using the function* syntax. When you invoke a generator function, it does not actually execute the function body, but instead returns a generator object. This generator object is an iterator. Calling its next() method causes the body of the generator function to run from the start (or whatever its current position is) until it reaches a yield statement.

yield is new in ES6 and is something like a return statement. The value of the yield statement becomes the value returned by the next() call on the iterator.

Here is an example of a generator function that yields the numbers 1 through 5:

function* numbersGenerator() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
}

const numbers = numbersGenerator();

To execute the generator function, we can call the next() method on the generator object.

console.log(numbers.next().value);
console.log(numbers.next().value);
console.log(numbers.next().value);

Output

1
2
3

 

Example-1: Accept arguments with Generators

Generators can also accept arguments and return values using the return() method.

function* addGenerator(x) {
    const y = yield;
    return x + y;
}

const add = addGenerator(5);
console.log(add.next());
console.log(add.next(7));

Output

{ value: undefined, done: false }
{ value: 12, done: true }

 

Example-2: Using waitForPromise with Generators

Generators can be used to simplify asynchronous code by allowing the function to pause and wait for a promise to resolve. For example, here is a generator function that waits for a promise to resolve before continuing execution

function* waitForPromise(promise) {
    const result = yield promise;
    return result;
}

const wait = waitForPromise(Promise.resolve(10));
wait.next().value.then((result) => console.log(wait.next(result).value));

Output

10

 

Example-3: Create infinite sequences

Generators can also be used to create infinite sequences using the yield* keyword. The yield* keyword allows the generator to delegate to another generator or iterable object.

Here is an example of a generator that yields an infinite sequence of numbers

function* countGenerator() {
    let i = 0;
    while (true) {
        yield i++;
    }
}

const count = countGenerator();
console.log(count.next().value); 
console.log(count.next().value); 
console.log(count.next().value);

Output

0
1
2

 

Example-4: Create custom iterators

Generators can be used to create custom iterators, as they have a built-in next() method. Here is an example of a generator that acts as an iterator for an array

function* arrayIterator(array) {
    for (const value of array) {
        yield value;
    }
}

const iterator = arrayIterator([1, 2, 3]);
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);

Output

1
2
3

 

Example-5: Return value of Generator function

The return value of the next() function is an object that has a value property and/or a done property. With typical iterators and generators, if the value property is defined, then the done property is undefined or is false. And if done is true, then value is undefined. But in the case of a generator that returns a value, the final call to next returns an object that has both value and done defined. The value property holds the return value of the generator function, and the done property is true, indicating that there are no more values to iterate. This final value is ignored by the for/of loop and by the spread operator, but it is available to code that manually iterates with explicit calls to next():

function *oneAndDone() {
    yield 1;
    return "done";
}

// The return value does not appear in normal iteration.
[...oneAndDone()]   // => [1]

// But it is available if you explicitly call next()
let generator = oneAndDone();
generator.next()           // => { value: 1, done: false}
generator.next()           // => { value: "done", done: true }
// If the generator is already done, the return value is not returned again
generator.next()           // => { value: undefined, done: true }

 

Summary

In summary, iterators and generators are useful tools in JavaScript for creating and iterating over sequences of values. They can be used to simplify asynchronous code and create custom iterators.

Let's summarise what we have learned:

  • An iterator object has a next() method that returns an iteration result object.
  • An iteration result object has a value property that holds the next iterated value, if there is one. If the iteration has completed, then the result object must have a done property set to true.
  • Generator functions (functions defined with function* instead of function) are another way to define iterators.
  • When you invoke a generator function, the body of the function does not run right away; instead, the return value is an iterable iterator object. Each time the next() method of the iterator is called, another chunk of the generator function runs.
  • Generator functions can use the yield operator to specify the values that are returned by the iterator. Each call to next() causes the generator function to run up to the next yield expression.
  • The value of that yield expression then becomes the value returned by the iterator. When there are no more yield expressions, then the generator function returns, and the iteration is complete.

 

References

Iterators and generators - JavaScript | MDN (mozilla.org)

 

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment