Introduction
Generators shine in specific scenarios: lazy evaluation, infinite sequences, state machines, and composable data transformations. Understanding these patterns helps you recognize when generators are the right tool.
Deep Dive
Lazy Evaluation
javascript// Only compute values when needed function* lazyMap(iterable, fn) { for (const item of iterable) { yield fn(item); } } function* lazyFilter(iterable, predicate) { for (const item of iterable) { if (predicate(item)) yield item; } } function* lazyTake(iterable, n) { let count = 0; for (const item of iterable) { if (count++ >= n) return; yield item; } } // Compose without creating intermediate arrays! function* hugeData() { for (let i = 0; i < 1000000; i++) yield i; } const result = lazyTake( lazyFilter( lazyMap(hugeData(), x => x * 2), x => x % 3 === 0 ), 5 ); [...result]; // First 5 even numbers divisible by 3
State Machine
javascriptfunction* trafficLight() { while (true) { yield 'green'; yield 'yellow'; yield 'red'; } } const light = trafficLight(); light.next().value; // 'green' light.next().value; // 'yellow' light.next().value; // 'red' light.next().value; // 'green' (cycles)
ID Generator
javascriptfunction* idGenerator(prefix = '') { let id = 0; while (true) { yield `${prefix}${++id}`; } } const userIds = idGenerator('user_'); userIds.next().value; // 'user_1' userIds.next().value; // 'user_2'
Cooperative Multitasking
javascriptfunction* longTask() { for (let i = 0; i < 1000; i++) { doExpensiveWork(i); if (i % 10 === 0) yield; // Yield control periodically } } // Run without blocking function runCooperatively(gen) { const iterator = gen(); function step() { const { done } = iterator.next(); if (!done) { setTimeout(step, 0); // Let other tasks run } } step(); }
Iterator Helpers Replace Manual Patterns (ES2025)
The manual lazyMap, lazyFilter, and lazyTake functions shown above are now built into the language:
javascript// Before (manual generator helpers): const result = lazyTake( lazyFilter( lazyMap(hugeData(), x => x * 2), x => x % 3 === 0 ), 5 ); // After (built-in iterator helpers): const result = hugeData() .map(x => x * 2) .filter(x => x % 3 === 0) .take(5) .toArray();
Built-in iterator helpers (.map(), .filter(), .take(), .drop(), .flatMap(), .reduce(), .forEach(), .some(), .every(), .find(), .toArray()) work directly on any iterator. They are lazy — no intermediate arrays are created.
Summary
Generators enable lazy evaluation, state machines, ID generation, and cooperative multitasking. Use them when you need on-demand computation, infinite sequences, or fine-grained control over execution flow.
Code Examples
javascript
// Only compute values when needed
function* lazyMap(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
function* lazyFilter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) yield item;
}
}
function* lazyTake(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count++ >= n) return;
yield item;javascript
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'
light.next().value; // 'green' (cycles)