
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.

Building a Vue Career in the Age of AI Coding Tools
How Junior Vue Developers Can Get Hired in 2026 and and what companies are looking for
Reza Baar
Aug 12, 2026

Everything, everywhere, all before you asked
There's always more than one way to hit the same goal. The whole game is the tradeoff you make, and why.
Alex Garrett-Smith
Aug 12, 2026

Nuxt 3 hits End Of Life
Nuxt 3 Hits EOL July 31, 2026: Your Staged Path to Nuxt 5 What the Nuxt 3 end-of-life date means, how to migrate through Nuxt 4, and when it makes sense to wait for Nuxt 5 instead.
Reza Baar
Aug 12, 2026