Lesson 7 · 7 of 20
Iterators and generators · JavaScript
Produce values on demand in JavaScript with function* and yield, and make your own objects work with for...of through Symbol.iterator.

A generator function is declared with function* and, instead of returning a value and finishing, it pauses at every yield. Calling invoiceNumbers("INV", 7) runs nothing yet: it returns a generator. Each next() resumes the code up to the next yield and hands back a { value, done } object, as the third line of output shows.
That is why a generator can be infinite without hanging the program: the condition-less for loop in invoiceNumbers only moves on when someone asks for another number. It is a handy way to produce values on demand: numbering schemes, pages from an API or lines from a file.
The iteration protocol is what for...of, the ... operator and destructuring rely on: they ask the object for its [Symbol.iterator]() method. Playlist defines it as a generator (*[Symbol.iterator]()), so its songs can be looped with for...of and copied into an array with [...party].
countdown shows what lies underneath, without generators: an iterator is just an object with a next() method that returns { value, done }. When done is true, the loop stops. Because [Symbol.iterator]() returns this, the object is both an iterable and an iterator.
Code · main.js
// A generator pauses at each yield and resumes on the next call to next().function* invoiceNumbers(prefix, start) { for (let n = start; ; n++) { yield `${prefix}-${String(n).padStart(4, "0")}`; }}const numbers = invoiceNumbers("INV", 7);console.log(numbers.next().value);console.log(numbers.next().value);console.log(numbers.next());// Any object with [Symbol.iterator] works with for...of and spread.class Playlist { constructor(...songs) { this.songs = songs; } *[Symbol.iterator]() { for (const song of this.songs) yield song.toUpperCase(); }}const party = new Playlist("intro", "hit single", "encore");for (const song of party) console.log("Playing", song);console.log([...party].length, "songs in the playlist");// A hand-written iterator: an object whose next() returns { value, done }.function countdown(from) { let current = from; return { [Symbol.iterator]() { return this; }, next: () => (current > 0 ? { value: current--, done: false } : { value: undefined, done: true }), };}console.log("Launch in", [...countdown(3)].join(", "));Output · node main.js
INV-0007
INV-0008
{ value: 'INV-0009', done: false }
Playing INTRO
Playing HIT SINGLE
Playing ENCORE
3 songs in the playlist
Launch in 3, 2, 1Key points
function*defines a generator;yieldhands out a value and pauses.- Each
next()returns{ value, done }. - A generator can be infinite because it only computes what is asked for.
- An object with
[Symbol.iterator]()works withfor...ofand....
Exercise
Write a take(iterable, n) generator that yields only the first n values of another iterable, and use it to get five invoice numbers with [...take(invoiceNumbers("INV", 1), 5)].