Generators & Iterators

Symbol.iterator, the iterator protocol, generator functions, lazy sequences, and async generators โ€” the machinery behind for...of and spread.

deep hard โฑ 20 min generatorsiteratorssymbol-iteratorasync-generatorslazy
Mastery:
Why interviewers ask this
Generators signal deep JS knowledge. They're the mechanism behind custom iterables, infinite sequences, and async iteration โ€” and they underpin how async/await was originally spec'd.

Generators and iterators are the protocol that powers for...of, spread ([...x]), and destructuring for any object โ€” including your own custom types.

The iterator protocol

An iterator is an object with a next() method that returns { value, done }. An iterable is any object with [Symbol.iterator]() that returns an iterator. Built-ins like Array, String, Map, Set, and NodeList implement this already.

const arr = [10, 20, 30];
const iter = arr[Symbol.iterator]();   // get the iterator
console.log(iter.next()); // { value: 10, done: false }
console.log(iter.next()); // { value: 20, done: false }
console.log(iter.next()); // { value: 30, done: false }
console.log(iter.next()); // { value: undefined, done: true }

Make any object iterable by adding [Symbol.iterator]:

const range = {
  from: 1, to: 5,
  [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, 4, 5]
for (const n of range) console.log(n); // 1 2 3 4 5

Generator functions

A generator function (function*) returns a generator object, which is both an iterator and an iterable. yield pauses execution and produces a value; the function resumes from that point on the next next() call.

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 

Infinite sequences โ€” the killer use case

Generators can produce values lazily on demand โ€” no array is ever fully materialized:

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 

yield* delegation

yield* delegates to another iterable, flattening it:

function* concat(...arrays) {
  for (const arr of arrays) yield* arr; // yield each item from arr
}
console.log([...concat([1,2], [3,4], [5])]); // [1,2,3,4,5]

Two-way communication โ€” next(value)

Generators can receive values back via next(value). The value becomes the result of the yield expression:

function* adder() {
  let sum = 0;
  while (true) {
    const n = yield sum;  // yield current sum, receive next number
    sum += n ?? 0;
  }
}
const g = adder();
g.next();      // start (first yield)
g.next(10);    // sum = 10
g.next(5);     // sum = 15
console.log(g.next(3).value); // 18

Async generators & for awaitโ€ฆof

Async generators yield promises asynchronously โ€” perfect for paginated APIs:

async function* paginate(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
    if (!data.items.length) return;
    yield data.items;
    page++;
  }
}

// Consume lazily
for await (const items of paginate('/api/posts')) {
  render(items);
  if (shouldStop()) break; // stop at any point โ€” no over-fetching
}

Say it out loud
โ€œAn iterator is any object with next() returning { value, done }. An iterable has [Symbol.iterator]() returning an iterator โ€” this is what powers for...of and spread. Generator functions (function*) produce iterators automatically: yield suspends execution and produces a value, and the function resumes on the next next() call. Generators shine for lazy sequences, infinite streams, and custom iteration. Async generators add await and work with for await...of for paginated or streaming data.โ€

Likely follow-up questions
  • What is the iterator protocol?
  • How do you make a custom object iterable?
  • What does yield* do?
  • How are generators related to async/await?
  • When would you use a generator over a simple array?

References