Skip to content

Generators

Generator functions can pause execution and resume later, yielding multiple values over time. Introduced in ES6 (2015), they use function* syntax and yield keyword.

Had to manually manage state for iterators:

function createRangeIterator(start, end) {
let current = start;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}

Generators simplify stateful iteration:

// Basic generator
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
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 }
// Range generator
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for (const num of range(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
// Infinite generator
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
// yield* delegates to another generator
function* gen1() {
yield 1;
yield 2;
}
function* gen2() {
yield* gen1();
yield 3;
}
[...gen2()]; // [1, 2, 3]
// Passing values to generator
function* doubler() {
while (true) {
const value = yield;
yield value * 2;
}
}
const gen = doubler();
gen.next(); // Start generator
console.log(gen.next(5).value); // 10
  • Lazy evaluation: Values computed on demand
  • Memory efficient: Don’t need full array in memory
  • Infinite sequences: Can represent unlimited data
  • State management: Built-in state between yields
  • Iteration protocol: Works with for…of, spread, etc.
  • Generator functions return generator objects
  • Must call next() to start execution
  • Can pass values back with next(value)
  • Can throw errors into generator with throw()
  • Can exit early with return()
  • yield* delegates to iterable
// Generator returns early
function* gen() {
yield 1;
return 2; // exits generator
yield 3; // never reached
}
[...gen()]; // [1] - return value not included
// Error handling
function* errorGen() {
try {
yield 1;
yield 2;
} catch (e) {
console.log('Error:', e);
}
yield 3;
}
const gen = errorGen();
gen.next(); // { value: 1, done: false }
gen.throw('oops'); // logs "Error: oops"
gen.next(); // { value: 3, done: false }
  1. Create a generator that yields even numbers from 0 to 10

    Answer
    function* evenNumbers() {
    for (let i = 0; i <= 10; i += 2) {
    yield i;
    }
    }
  2. Convert generator to array: function* gen() { yield 1; yield 2; }

    Answer
    const arr = [...gen()];
    // or
    const arr = Array.from(gen());
  3. Why use generators instead of arrays?

    Answer Memory efficiency (lazy evaluation), can represent infinite sequences, better for large datasets or expensive computations.