Generators
Generators
Section titled “Generators”What it is
Section titled “What it is”Generator functions can pause execution and resume later, yielding multiple values over time. Introduced in ES6 (2015), they use function* syntax and yield keyword.
Before this feature
Section titled “Before this feature”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 }; } };}After this feature
Section titled “After this feature”Generators simplify stateful iteration:
// Basic generatorfunction* 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 generatorfunction* 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 generatorfunction* fibonacci() { let [a, b] = [0, 1]; while (true) { yield a; [a, b] = [b, a + b]; }}
const fib = fibonacci();console.log(fib.next().value); // 0console.log(fib.next().value); // 1console.log(fib.next().value); // 1console.log(fib.next().value); // 2
// yield* delegates to another generatorfunction* gen1() { yield 1; yield 2;}
function* gen2() { yield* gen1(); yield 3;}
[...gen2()]; // [1, 2, 3]
// Passing values to generatorfunction* doubler() { while (true) { const value = yield; yield value * 2; }}
const gen = doubler();gen.next(); // Start generatorconsole.log(gen.next(5).value); // 10Why this is better
Section titled “Why this is better”- 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.
Key notes / edge cases
Section titled “Key notes / edge cases”- 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 earlyfunction* gen() { yield 1; return 2; // exits generator yield 3; // never reached}
[...gen()]; // [1] - return value not included
// Error handlingfunction* 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 }Quick practice
Section titled “Quick practice”-
Create a generator that yields even numbers from 0 to 10
Answer
function* evenNumbers() {for (let i = 0; i <= 10; i += 2) {yield i;}} -
Convert generator to array:
function* gen() { yield 1; yield 2; }Answer
const arr = [...gen()];// orconst arr = Array.from(gen()); -
Why use generators instead of arrays?
Answer
Memory efficiency (lazy evaluation), can represent infinite sequences, better for large datasets or expensive computations.