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?
Thefor/ofloop 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 theiterableobjects: these are types like Array, Set, and Map that can be iterated.
- Second, there is theiteratorobject itself, which performs the iteration.
- And third, there is theiteration resultobject 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 itsnext()method causes the body of the
generator function to run from the start (or whatever its current
position is) until it reaches ayieldstatement.
yieldis new in ES6 and is something like areturnstatement. The
value of theyieldstatement becomes the value returned by
thenext()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 thenext()function is an object that has
avalueproperty and/or adoneproperty. With typical iterators and
generators, if thevalueproperty is defined, then thedoneproperty
is undefined or isfalse. And ifdoneistrue, thenvalueis
undefined. But in the case of a generator that returns a value, the
final call tonextreturns an object that has
bothvalueanddonedefined. Thevalueproperty holds the return
value of the generator function, and thedoneproperty istrue,
indicating that there are no more values to iterate. This final value is
ignored by thefor/ofloop and by the spread operator, but it is
available to code that manually iterates with explicit calls
tonext():
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
valueproperty that holds the next iterated value, if there is one. If the iteration has completed, then the result object must have adoneproperty set totrue. - Generator functions (functions defined with
function*instead offunction) 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
yieldoperator to specify the values that are returned by the iterator. Each call tonext()causes the generator function to run up to the nextyieldexpression. - The value of that
yieldexpression then becomes the value returned by the iterator. When there are no moreyieldexpressions, then the generator function returns, and the iteration is complete.

![JavaScript Iterator and Generator [In-Depth Tutorial]](/javascript-iterator-and-generator/javascript-iterators.jpg)