Generators
Generators are functions that can pause and resume execution. They provide an easier way to create iterators.
Generator Syntax
Use function* to define a generator and yield to pause:
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
// Generators are iterable
for (const value of simpleGenerator()) {
console.log(value); // 1, 2, 3
}
Generator with Logic
function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
console.log([...range(1, 10, 2)]); // [1, 3, 5, 7, 9]
for (const num of range(5, 8)) {
console.log(num); // 5, 6, 7, 8
}
Infinite Generators
A generator can produce an infinite sequence. Each value is evaluated lazily, which means it is computed only when we ask for the next one:
function* infiniteCounter() {
let i = 0;
while (true) {
yield i++;
}
}
const counter = infiniteCounter();
console.log(counter.next().value); // 0
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
// ... continues forever
// Take first 5
function* take(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count++ >= n) return;
yield item;
}
}
console.log([...take(infiniteCounter(), 5)]); // [0, 1, 2, 3, 4]
Fibonacci Generator
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
const fib = fibonacci();
for (let i = 0; i < 10; i++) {
console.log(fib.next().value);
}
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
Generator Methods
Delegating with yield*
Delegate to another generator or iterable:
function* gen1() {
yield 1;
yield 2;
}
function* gen2() {
yield* gen1(); // Delegate to gen1
yield 3;
yield* [4, 5]; // Delegate to array
}
console.log([...gen2()]); // [1, 2, 3, 4, 5]
Passing Values to Generators
The next() method can pass values back into the generator:
function* conversation() {
const name = yield "What is your name?";
const age = yield `Hello ${name}! How old are you?`;
yield `${name} is ${age} years old.`;
}
const chat = conversation();
console.log(chat.next().value); // "What is your name?"
console.log(chat.next("Alice").value); // "Hello Alice! How old are you?"
console.log(chat.next(25).value); // "Alice is 25 years old."
Generator Return and Throw
function* gen() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log("Cleanup");
}
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return(99)); // Cleanup, { value: 99, done: true }
// Or throw an error into the generator
const g2 = gen();
g2.next();
// g2.throw(new Error('Something went wrong'));