Iterators and generators

The iterator protocol

An iterator is an object with a next method that returns a value plus a done flag. Loops call next until done becomes true, so any object with that shape can drive a loop.

Iterables add a computed method keyed by Symbol.iterator that creates a fresh iterator. Arrays, maps, sets, and strings already follow this contract out of the box.

const nums = [10, 20, 30]; const it = nums[Symbol.iterator](); console.log(it.next());

function* generators

Writing next by hand gets tedious, so generator functions build iterators for you. A function star returns a paused sequence that resumes on each next call.

Use generators when the sequence has logic: counters, ranges, retries, or paged reads. The function body reads top to bottom while callers consume values one by one.

function* countUp(limit: number): Generator<number> { for (let i = 1; i <= limit; i++) { yield i; } } for (const n of countUp(3)) { console.log(n); }

Yield values

Yield pauses the generator and hands one value to the caller. On the next request the function resumes right after that yield with all locals intact.

Yield delegation with yield star forwards to another generator. It keeps each sequence short while still exposing one flat stream to the loop.

function* pair(): Generator<string> { yield "north"; yield "south"; } function* trip(): Generator<string> { yield* pair(); yield "home"; }

Lazy pipelines

Generators shine in pipelines because each stage pulls only what it needs. Map and take helpers stay lazy, so a huge range never materializes in memory at once.

Build the pipeline from small generators, then consume with a plain for loop. The code reads as data flow while the runtime does minimal work per step.

function* take(source: Generator<number>, n: number): Generator<number> { let seen = 0; for (const v of source) { if (seen >= n) break; seen++; yield v * 2; } }

Next: Mixins

Article author: Arthur Isaev

Banner image