
JavaScript generators explained: write lazy iterators with yield, handle infinite sequences, and skip the protocol boilerplate.
Martin Ferret
July 21, 2026
JavaScript generators have existed since ES2015, yet many developers still treat yield as an exotic feature. It solves a common problem: producing values on demand, one at a time, without building the whole sequence in memory. A generator lets you write an iterator with ordinary control flow, pausing and resuming a function like a paused video.
Writing a custom iterator by hand means implementing the protocol yourself: an object with a next() method returning { value, done }, with state threaded through closures.
`function range(start, end) {
let current = start;
return {
Symbol.iterator { return this; },
next() {
return current < end
? { value: current++, done: false }
: { value: undefined, done: true };
},
};
}`
A generator expresses the same logic as the control flow it actually is:
`function* range(start, end) {
for (let i = start; i < end; i++) yield i;
}`
No next, no done, no protocol object. The engine writes that for you.
Declared with function*. Calling it does not run the body: it returns a generator object. Execution advances only when you pull a value.
`function* letters() {
yield 'a';
yield 'b';
}
const g = letters(); g.next(); // { value: 'a', done: false } g.next(); // { value: 'b', done: false } g.next(); // { value: undefined, done: true }`
Each next() runs to the next yield, returns that value, and freezes. Locals and position are preserved between calls.
Because the function is paused rather than finished, you can write sequences that never end:
`function* naturals() {
let i = 1;
while (true) yield i++;
}`
The while (true) is safe: it only advances one step per next().
yield* and next(value)yield* delegates to another iterable, forwarding all its values:
`function* outer() {
yield 0;
yield* [1, 2];
yield 3;
}
[...outer()]; // [0, 1, 2, 3]`
The argument passed to next becomes the result of the yield where the generator is paused, making it a two-way channel. Note: the first next() argument is ignored, since no yield is waiting yet.
`function* ask() {
const name = yield 'name?';
return `hi ${name}`;
}
const a = ask();
a.next().value; // 'name?'
a.next('Ada').value; // 'hi Ada'`
A generator returns itself from [Symbol.iterator], so it drops into for...of, spread, and destructuring with no conversion. It also chains with the new iterator helpers:
`naturals().filter((x) => x % 2 === 0).take(3).toArray(); // [2, 4, 6]`
An infinite source, processed safely, because take(3) stops the pipeline.
Single-use. Once exhausted, a generator stays exhausted. Call the function again for a fresh one.
for...of discards the return value. A return x sets { value: x, done: true }, but loops and spread stop at done and never see it.
Arrow functions cannot be generators. Always function* or a *method().
Spreading an infinite generator hangs. Bound it with take before draining it.
Generators are Baseline widely available. They shipped with ES2015 and have worked in every major engine for close to a decade: Chrome 39+, Firefox 26+, Safari 10+, Node 4+. TypeScript types them fully. Async generators (async function*) extend the same idea to asynchronous streams since ES2018.
Generators add no new power, a hand-written iterator can do the same. What yield changes is how much machinery stands between you and the sequence. Manual done flags become ordinary control flow, infinite sequences become a safe while (true), and composition becomes one yield*. For custom iteration, lazy sequences, tree walks, or ID factories, reaching for a generator is a clean win.
Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

JavaScript Generators: Writing Iterators With yield
JavaScript generators explained: write lazy iterators with yield, handle infinite sequences, and skip the protocol boilerplate.
Martin Ferret
Jul 21, 2026

Designing Loading UI in React
A practical guide to loading UI in React: Suspense boundaries, skeletons vs spinners, avoiding layout shift, and animating reveals.
Aurora Scharff
Jul 16, 2026

Performance Optimization in Nuxt
Practical techniques for faster Nuxt apps
Reza Baar
Jul 15, 2026