Generators
Introduction
A generator is a function that can pause and resume. It is declared with function*, produces values with yield, and returns an iterator whose next() method advances execution.
Generators make lazy sequences natural: ranges, paginated adapters, infinite counters, parser streams, cooperative workflows, and custom iteration protocols.
Why This Matters
Interviewers use generators to test execution suspension, the iterator protocol, and lazy evaluation. They also connect cleanly to infinite sequences and composing iterables with yield*.
Theory
Execution model
Calling a generator function does not run its body immediately. It returns a generator object. The body starts only when next() is called. Each yield pauses execution and returns { value, done: false }. A return or falling off the end returns { value, done: true }.
Lazy sequences
Generators compute values on demand. That means you can model large or infinite sequences without allocating all values up front. Consumers decide how many values to take.
Sending values back in
next(value) sends a value into the paused generator as the result of the current yield expression. The first next starts the generator and its argument is ignored.
Delegation with yield*
yield* iterable forwards iteration to another iterable. It is useful for flattening generators or composing smaller sequence producers.
Visual Diagrams
call generator -> returns iterator, body not run next() -> run until first yield -> pause next() -> resume after yield -> pause at next yield next() -> resume -> return done true
The call stack re-enters the generator body on each `next()`.
Code Examples
Finite and infinite generators
The infinite generator is safe because consumers can stop after taking a limited number of values.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1function* steps() {2 console.log('start');3 yield 'A';4 console.log('middle');5 yield 'B';6 return 'C';7}8 9var iterator = steps();10console.log('created');11console.log(iterator.next().value);12console.log(iterator.next().value);13console.log(iterator.next().value);Coding Exercises
Build lazy range and take helpers
MediumImplement range(start, end, step) as a generator and take(iterable, count) as a helper that consumes at most count values from any iterable.
Interview Questions
1What happens when you call a generator function?
The body does not execute immediately. The call returns a generator object that implements the iterator protocol. Execution begins on the first next() and pauses at each yield.
Follow-ups
- What does `next(value)` do?
- How does `yield*` work?
2Why are generators useful for infinite sequences?
They produce values lazily. Only requested values are computed, so an infinite sequence can be represented safely as long as consumers stop after a finite number of next() calls.
Quiz
1. When does a generator function body start executing?
Summary
- Generators are declared with `function*` and pause at `yield`.
- Calling a generator returns an iterator without running the body immediately.
- Generators are ideal for lazy and infinite sequences.
- `yield*` delegates iteration to another iterable.
Cheat Sheet
Declare: function* name().
Pause: yield value returns { value, done: false }.
Finish: return value returns { value, done: true }.
Lazy: body runs only as consumers call next().
Compose: yield* otherIterable.