Iterators & Generators

Medium100% Free~25 mins#iterators#generators#symbol-iterator#yield#resumable-functions#es6#lazy-evaluation
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}
6
7const gen = stepSequence(5);
8
9const v1 = gen.next().value;
10const v2 = gen.next(20).value;
11const r3 = gen.next();
12const v3 = r3.value;
13const v4 = gen.next().value;
14
15console.log(v1, v2, v3, String(v4));
Predict Console Output
Interactive Challenge

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.

Call Stack (Top = Active)
Global Execution Context
stepSequence(5)
Lexical Scope / Bindings
gen:Generator { <suspended> }
Console Stream
> [empty]

Deep Technical Breakdown

The Iteration Protocols

ES6 introduced two complementary iteration protocols that standardize traversal across JavaScript data structures:

  1. 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, and NodeList.
  2. 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: true is reached, subsequent .next() calls should continue returning { value: undefined, done: true }.

Generator Functions & yield

Generators (function*) are special functions that can pause and resume execution:

  • yield expr: Suspends the generator's execution context and sends expr out to the caller.
  • gen.next(arg): Resumes the generator. The paused yield expression inside the generator evaluates to arg! 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 no yield expression 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!

javascript
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

javascript
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 active finally blocks and returning { value: val, done: true }.
  • gen.throw(err): Injects an exception into the generator at the current yield pause point, allowing try...catch inside 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?