A generator is a special function that can pause in the middle and resume later. It is declared with function* and gives out values one at a time with yield.
function* count() {
console.log("start");
yield 1;
console.log("after first yield");
yield 2;
console.log("after second yield");
yield 3;
}
const generator = count(); // nothing runs yet
console.log(generator.next()); // → { value: 1, done: false } (runs until the first yield)
console.log(generator.next()); // → { value: 2, done: false }
console.log(generator.next()); // → { value: 3, done: false }
console.log(generator.next()); // → { value: undefined, done: true }
Calling a generator function does not run its body. It returns a generator object (which is an iterator). Each next() runs the code until the next yield, hands the value out, and pauses there.
Using it in loops
A generator object is iterable, so for...of and spread work.
function* letters() {
yield "a";
yield "b";
yield "c";
}
for (const letter of letters()) {
console.log(letter);
}
console.log([...letters()]); // → ["a", "b", "c"]
Infinite sequences
Because values are produced on demand, a generator can describe an endless sequence without using endless memory.
function* naturals() {
let n = 1;
while (true) {
yield n++;
}
}
for (const n of naturals()) {
if (n > 5) break; // stop it yourself!
console.log(n); // 1, 2, 3, 4, 5
}
(The editor stops after 300 console lines, but an infinite loop without break would freeze a normal page. Always have a way out.)
Taking just what you need
function* naturals() {
let n = 1;
while (true) yield n++;
}
function take(iterable, count) {
const result = [];
for (const item of iterable) {
if (result.length >= count) break;
result.push(item);
}
return result;
}
console.log(take(naturals(), 4)); // → [1, 2, 3, 4]
yield* delegates to another iterable
function* abc() {
yield "a";
yield "b";
}
function* all() {
yield* abc(); // yields everything from abc()
yield "c";
}
console.log([...all()]); // → ["a", "b", "c"]
Generators are lazy
Nothing is computed until someone asks for the next value. That is the key idea, and the reason to use them: big or infinite data, and step-by-step processes.
Syntax reminders
function* name() is the common style. In object and class methods, write *name() { ... }.