Iterators & Generators
Key Learning Objectives
Understand the Iterable Protocol ([Symbol.iterator]) and the Iterator Protocol (next() returning { value, done }).
Master generator functions (function*) and the mechanics of resumable, cooperative execution via yield.
Learn bidirectional data exchange: passing values into generators via next(argument).
Identify iterator exhaustion behavior and understand why for...of discards the return value of a generator.
Implement infinite sequences and lazy data pipelines with O(1) memory overhead.
The Interview Problem
What is logged to the console when the generator is driven step-by-step with next(), and how do yield argument injection and iterator exhaustion operate?
1function* stepSequence(start) {2 const step1 = yield start * 2;3 const step2 = yield (step1 || 1) + 10;4 return 'DONE';5}67const gen = stepSequence(5);89const v1 = gen.next().value;10const v2 = gen.next(20).value;11const r3 = gen.next();12const v3 = r3.value;13const v4 = gen.next().value;1415console.log(v1, v2, v3, String(v4));
Predict Console Output
Select the option that matches what standard ECMAScript prints to the console:
10 30 DONE undefined
10 11 DONE DONE
10 30 undefined undefined
undefined 30 DONE undefined
V8 Engine Execution Trace
Step 1 of 6 (Line 7)Calling stepSequence(5) does not execute the function body; it returns a Generator iterator in a suspended state.
Deep Technical Breakdown
The Iteration Protocols
ES6 introduced two complementary iteration protocols that standardize traversal across JavaScript data structures:
- The Iterable Protocol:
- An object is iterable if it implements a method with key
[Symbol.iterator]. - This method must return an iterator object.
- Built-in iterables include
Array,String,Map,Set,TypedArray, andNodeList.
- An object is iterable if it implements a method with key
- The Iterator Protocol:
- An object is an iterator if it implements a
.next()method. .next()returns an object with{ value: any, done: boolean }.- Once
done: trueis reached, subsequent.next()calls should continue returning{ value: undefined, done: true }.
- An object is an iterator if it implements a
Generator Functions & yield
Generators (function*) are special functions that can pause and resume execution:
yield expr: Suspends the generator's execution context and sendsexprout to the caller.gen.next(arg): Resumes the generator. The pausedyieldexpression inside the generator evaluates toarg! This enables two-way communication between caller and callee.- First
next()Call Rule: Any argument passed to the initial.next(arg)call is ignored by the JavaScript engine because noyieldexpression is currently waiting to receive a value.
for...of vs Generator Return
A critical interview nuance: for...of loops discard the return value of a generator!
function* demo() {
yield 1;
yield 2;
return 3;
}
for (const val of demo()) {
console.log(val); // Logs: 1, then 2 (3 is NEVER logged!)
}Because for...of terminates the loop immediately upon seeing { done: true }, any value attached to the closing object is ignored. If you want a value consumed by for...of, you must yield it.
Custom Iterable Example
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range]); // [1, 2, 3]Generator Methods: return() and throw()
gen.return(val): Forces the generator to terminate immediately, executing any activefinallyblocks and returning{ value: val, done: true }.gen.throw(err): Injects an exception into the generator at the currentyieldpause point, allowingtry...catchinside the generator to intercept it.
Common Traps & Mistakes
Passing an argument to the very first `gen.next(value)` call and expecting it to be captured by the first `yield`. The first call simply starts execution.
Expecting `for...of` or spread (`[...gen()]`) to include the value from a generator's `return` statement. The `done: true` value is always skipped by iteration protocols.
Attempting to reuse an exhausted generator. Once a generator returns `done: true`, it cannot be restarted; a new generator instance must be created.
Confusing `yield*` (delegating yield to another iterable) with normal `yield`.
FAANG Follow-Up Probes
Probe #1
How does `yield*` delegate iteration to another generator or iterable (e.g. `yield* [1, 2, 3]`)?
Probe #2
How did libraries like `co` or `redux-saga` use generators and Promises to implement async/await before ES2017?
Probe #3
How do Asynchronous Generators (`async function*` with `for await...of`) stream chunks of data from network streams?
