Iterators and Generators in JavaScript

Master the iterator protocol, generator functions, and ES2025 Iterator Helpers to write more efficient, expressive JavaScript code.

Iterators and Generators bring the concept of iteration directly into the core JavaScript language, providing a powerful mechanism for customizing the behavior of for...of loops. These ES6 features enable developers to create custom iteration patterns, implement lazy evaluation, and build efficient data processing pipelines that are essential for modern web development with frameworks like Next.js.

Understanding iterators and generators is crucial for writing clean, performant JavaScript that handles data streams efficiently and maintains optimal Core Web Vitals scores. Our /services/web-development/ team regularly applies these patterns when building scalable applications that process large datasets without memory overhead.

Understanding the Iterator Protocol

An iterator is any object which implements the Iterator protocol by having a next() method that returns an object with two properties:

  • value: The next value in the iteration sequence
  • done: true if the last value in the sequence has already been consumed

Once created, an iterator object can be iterated explicitly by repeatedly calling next(). Iterating over an iterator is said to consume the iterator, because it is generally only possible to do once. After a terminating value has been yielded, additional calls to next() should continue to return {done: true}.

Creating Custom Iterators

While it is easy to imagine that all iterators could be expressed as arrays, this is not true. Arrays must be allocated in their entirety, but iterators are consumed only as necessary. Because of this, iterators can express sequences of unlimited size, such as the range of integers between 0 and Infinity.

An iterator is any object which implements the Iterator protocol by having a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols" target="_blank">next() method that returns an object with two properties</a>: value (the next value in the iteration sequence) and done (true if the last value has been consumed).

Custom Range Iterator
1function makeRangeIterator(start = 0, end = Infinity, step = 1) {2 let nextIndex = start;3 let iterationCount = 0;4 5 const rangeIterator = {6 next() {7 let result;8 if (nextIndex < end) {9 result = { value: nextIndex, done: false };10 nextIndex += step;11 iterationCount++;12 return result;13 }14 return { value: iterationCount, done: true };15 },16 };17 return rangeIterator;18}19 20// Usage21const iter = makeRangeIterator(1, 10, 2);22let result = iter.next();23while (!result.done) {24 console.log(result.value); // 1, 3, 5, 7, 925 result = iter.next();26}27console.log('Iterated over sequence of size:', result.value); // 5

Generator Functions: Simplified Iterator Creation

While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generator functions provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function whose execution is not continuous.

How Generators Work

Generator functions are written using the function* syntax. When called, generator functions do not initially execute their code. Instead, they return a special type of iterator, called a Generator. When a value is consumed by calling the generator's next() method, the Generator function executes until it encounters the yield keyword.

Generator functions allow you to define an iterative algorithm by writing a single function whose execution is not continuous. When you call a generator, it returns a generator object which conforms to both the iterator and iterable protocols. Each Generator may only be iterated once--if you need to iterate multiple times, you must call the generator function again to get a new generator.

<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators" target="_blank">Generator functions provide a powerful alternative for creating iterators</a>, allowing developers to write cleaner, more maintainable code with implicit state management. When building /services/web-development/ solutions that require efficient data processing, generators offer significant advantages over traditional array-based approaches.

Generator Function Example
1function* makeRangeIterator(start = 0, end = Infinity, step = 1) {2 let iterationCount = 0;3 for (let i = start; i < end; i += step) {4 iterationCount++;5 yield i;6 }7 return iterationCount;8}9 10// Usage - much cleaner than manual iterator11const iter = makeRangeIterator(1, 10, 2);12for (const value of iter) {13 console.log(value); // 1, 3, 5, 7, 914}

The Iterable Protocol

An object is iterable if it defines its iteration behavior, such as what values are looped over in a for...of construct. Some built-in types, such as Array or Map, have a default iteration behavior, while other types (such as Object) do not.

Implementing Symbol.iterator

In order to be iterable, an object must implement the [Symbol.iterator]() method. This means that the object (or one of the objects up its prototype chain) must have a property with a Symbol.iterator key.

An object is iterable if it defines its iteration behavior--what values are looped over in a for...of construct. Some built-in types like Array and Map have default iteration behavior, while others like Object do not. An object must implement the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols" target="_blank">Symbol.iterator method</a> to be iterable.

It may be possible to iterate over an iterable more than once, or only once. It is up to the programmer to know which is the case. Iterables which can iterate only once (such as Generators) customarily return this from their [Symbol.iterator]() method, whereas iterables which can be iterated many times must return a new iterator on each invocation of [Symbol.iterator]() (like arrays).

User-Defined Iterable
1const myIterable = {2 *[Symbol.iterator]() {3 yield 1;4 yield 2;5 yield 3;6 },7};8 9// Works with for...of10for (const value of myIterable) {11 console.log(value); // 1, 2, 312}13 14// Works with spread syntax15[...myIterable]; // [1, 2, 3]16 17// Works with destructuring18const [first, second, third] = myIterable;19// first = 1, second = 2, third = 3

Built-in Iterables

JavaScript provides several built-in iterables that you'll use frequently:

TypeIteration Behavior
StringIterates over Unicode code points
ArrayIterates over elements in insertion order
TypedArrayIterates over typed array elements
MapIterates over key-value pairs in insertion order
SetIterates over set elements in insertion order

All of these types have a Symbol.iterator method on their prototype objects, making them directly usable with for...of loops and other iterable-consuming syntax.

Built-in iterables in JavaScript include <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators" target="_blank">String (iterates over Unicode code points), Array (iterates over elements in insertion order), TypedArray (iterates over typed array elements), Map (iterates over key-value pairs in insertion order), and Set (iterates over set elements in insertion order)</a>. The arguments object and NodeList objects are also iterable.

Syntaxes Expecting Iterables

JavaScript provides several syntax constructs that work with iterables:

for...of Loops

Creates a loop iterating over iterable objects:

for (const value of ['a', 'b', 'c']) {
 console.log(value); // 'a', 'b', 'c'
}

Spread Syntax

Expands iterables into individual elements:

[...'abc']; // ['a', 'b', 'c']
const set = new Set([1, 2, 3]);
[...set]; // [1, 2, 3]

Destructuring Assignment

Works with any iterable:

const [a, b, c] = new Set(['a', 'b', 'c']);
// a = 'a', b = 'b', c = 'c'

yield* Delegation

Delegates to another iterable:

function* gen() {
 yield* ['a', 'b', 'c'];
}

gen().next(); // { value: 'a', done: false }

The <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of" target="_blank">for...of statement creates a loop iterating over iterable objects</a>, including built-in String, Array, TypedArray, Map, Set, user-defined iterables, and objects with [Symbol.iterator] defined.

Advanced Generator Features

Passing Values to next()

The next() method also accepts a value, which can be used to modify the internal state of the generator. A value passed to next() will be received by yield:

function* counter() {
 const step = yield 0;
 for (let i = 1; i <= step; i++) {
 yield i;
 }
}

const gen = counter();
gen.next(); // { value: 0, done: false }
gen.next(5); // step = 5, { value: 1, done: false }
gen.next(); // { value: 2, done: false }

yield* for Delegation

The yield* expression delegates to another generator or iterable:

function* inner() {
 yield 1;
 yield 2;
}

function* outer() {
 yield 'a';
 yield* inner();
 yield 'b';
}

[...outer()]; // ['a', 1, 2, 'b']

Return and Throw Methods

Generators also have return() and throw() methods for terminating iteration or injecting errors into the generator function. These patterns are particularly valuable when building robust /services/web-development/ applications that need to handle errors gracefully in data processing pipelines.

ES2025 Iterator Helpers

JavaScript 2025 (ECMAScript 2025) officially approved a set of new Iterator Helper methods that significantly enhance the functionality of iterators. These built-in methods allow you to chain operations on iterators without converting them to arrays first, maintaining lazy evaluation for better performance.

Available Iterator Helpers

MethodDescription
.map(fn)Transforms each value
.filter(fn)Filters values by predicate
.take(n)Limits to n values
.drop(n)Skips first n values
.reduce(fn, initial)Reduces to single value
.toArray()Converts to array
.flatMap(fn)Maps then flattens
.every(fn)Tests all pass predicate
.some(fn)Tests any pass predicate
.find(fn)Finds first match
.forEach(fn)Executes for each value

These methods allow you to chain operations on iterators without converting them to arrays first, maintaining lazy evaluation for better performance.

<a href="https://www.w3schools.com/js/js_iterators.asp" target="_blank">ES2025 Iterator Helper methods</a> include .map(fn), .filter(fn), .take(n), .drop(n), .reduce(fn, initial), .toArray(), .flatMap(fn), .every(fn), .some(fn), .find(fn), and .forEach(fn). For teams exploring advanced JavaScript patterns, these helpers complement our /services/ai-automation/ capabilities when building intelligent data processing systems that need to handle streaming data efficiently.

ES2025 Iterator Helper Methods
1// Creating an iterator from a generator2function* numbers() {3 yield 1;4 yield 2;5 yield 3;6 yield 4;7 yield 5;8}9 10const iter = numbers();11 12// Chaining iterator helpers (lazy evaluation)13const result = iter14 .filter(n => n % 2 === 1) // Keep odd numbers15 .map(n => n * 2) // Double them16 .toArray(); // Convert to array17 18// result = [2, 6, 10]19 20// Other helpers21iter.take(3); // Limit to 3 values22iter.drop(1); // Skip first value23iter.every(n => n > 0); // Check all values24iter.some(n => n > 10); // Check any value25iter.forEach(console.log); // Execute for each
Performance Benefits

Why iterators and generators matter for modern JavaScript applications

Lazy Evaluation

Generators compute their yielded values on demand, enabling efficient representation of expensive or infinite sequences without allocating memory upfront.

Memory Efficiency

Process large datasets or streams without loading everything into memory, crucial for handling APIs and large files in web applications.

Cleaner Code

Generator functions provide a more readable and maintainable alternative to manual iterator implementation with explicit state management.

Composable Patterns

Build data processing pipelines by chaining generators and iterator helpers, creating clear and reusable transformation logic.

Practical Use Cases

Infinite Sequences

function* naturalNumbers() {
 let n = 1;
 while (true) {
 yield n++;
 }
}

const numbers = naturalNumbers();
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// Can continue indefinitely!

Data Processing Pipelines

function* processData(data) {
 for (const item of data) {
 const transformed = item.trim().toUpperCase();
 if (transformed.length > 0) {
 yield transformed;
 }
 }
}

const results = [...processData([' a ', 'bb', '', 'ccc'])]
// ['A', 'BB', 'CCC']

State Machines

function* trafficLight() {
 while (true) {
 yield 'green';
 yield 'yellow';
 yield 'red';
 }
}

const light = trafficLight();
light.next().value; // 'green'
light.next().value; // 'yellow'
light.next().value; // 'red'

Async Data Streams

async function* fetchMessages(url) {
 let page = 1;
 
 while (true) {
 const response = await fetch(`${url}?page=${page}`);
 const messages = await response.json();
 
 if (messages.length === 0) break;
 
 for (const message of messages) {
 yield message;
 }
 
 page++;
 }
}

// Usage
for await (const message of fetchMessages('/api/messages')) {
 console.log(message);
}

Our /resources/guides/web-development/async-js/ guide covers async iteration patterns in more detail, including how to combine generators with async/await for powerful streaming data solutions.

When to Use Generators

  • Processing large or infinite data streams
  • Implementing state machines
  • Creating custom iteration logic
  • Building data processing pipelines
  • Lazy evaluation of expensive computations
  • Implementing async iteration patterns

When to Use Arrays

  • Need random access to elements
  • Multiple passes over data
  • Data size is known and manageable
  • Need array methods like sort, splice

Frequently Asked Questions

Build Better JavaScript Applications

Our team specializes in modern JavaScript development using Next.js and ES6+ features to create performant, scalable web applications.

Sources

  1. MDN Web Docs - Iterators and generators - Comprehensive official documentation
  2. MDN Web Docs - Iteration Protocols - Iterator protocol specification
  3. MDN Web Docs - for...of - Loop syntax documentation
  4. LogRocket - A comprehensive guide to JavaScript generators - Practical developer guide
  5. W3Schools - JavaScript Iterators - ES2025 Iterator Helpers reference