JavaScript Generators

Functions that can pause and resume execution

⚑ What are Generators?

Generators are special functions that can pause their execution and resume later. They use the function* syntax and yield keyword to produce a sequence of values.


// Basic generator function
function* simpleGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = simpleGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
                                    

Output:

1
2
3

Generator Features

⏸️

Pausable

Can pause and resume execution

function* gen() {
    yield 1; // Pause here
    yield 2; // Resume and pause
}
πŸ”„

Lazy Evaluation

Values computed on demand

function* infinite() {
    let i = 0;
    while (true) yield i++;
}
πŸ’Ύ

State Preservation

Remembers local variables

function* counter() {
    let count = 0;
    while (true) yield ++count;
}
πŸ”

Iterable

Works with for...of loops

for (const value of gen()) {
    console.log(value);
}

πŸ”Ή Basic Generator Syntax

Generators use function* and yield keywords:

// Generator function declaration
function* numberGenerator() {
    console.log('Generator started');
    yield 1;
    console.log('After first yield');
    yield 2;
    console.log('After second yield');
    yield 3;
    console.log('Generator finished');
}

// Create generator object
const gen = numberGenerator();

// Call next() to execute until next yield
console.log('Calling first next()');
console.log(gen.next()); // {value: 1, done: false}

console.log('Calling second next()');
console.log(gen.next()); // {value: 2, done: false}

console.log('Calling third next()');
console.log(gen.next()); // {value: 3, done: false}

console.log('Calling fourth next()');
console.log(gen.next()); // {value: undefined, done: true}

Output:

Calling first next()
Generator started
{value: 1, done: false}
Calling second next()
After first yield
{value: 2, done: false}
Calling third next()
After second yield
{value: 3, done: false}
Calling fourth next()
After third yield
Generator finished
{value: undefined, done: true}

πŸ”Ή Generators with for...of

Generators are iterable and work perfectly with for...of:

function* fruitGenerator() {
    yield 'apple';
    yield 'banana';
    yield 'orange';
}

// Use with for...of loop
console.log('Using for...of:');
for (const fruit of fruitGenerator()) {
    console.log(fruit);
}

// Convert to array
const fruits = [...fruitGenerator()];
console.log('As array:', fruits);

Output:

Using for...of:
apple
banana
orange
As array: ['apple', 'banana', 'orange']

πŸ”Ή Infinite Sequences

Generators can create infinite sequences efficiently:

// Infinite number sequence
function* infiniteNumbers() {
    let num = 1;
    while (true) {
        yield num++;
    }
}

// Fibonacci sequence generator
function* fibonacci() {
    let a = 0, b = 1;
    while (true) {
        yield a;
        [a, b] = [b, a + b];
    }
}

// Use infinite generators (with limits!)
console.log('First 5 numbers:');
const numbers = infiniteNumbers();
for (let i = 0; i < 5; i++) {
    console.log(numbers.next().value);
}

console.log('First 8 Fibonacci numbers:');
const fib = fibonacci();
for (let i = 0; i < 8; i++) {
    console.log(fib.next().value);
}

Output:

First 5 numbers:
1
2
3
4
5
First 8 Fibonacci numbers:
0
1
1
2
3
5
8
13

πŸ”Ή Generator with Parameters

You can pass values back into generators using next():

function* interactiveGenerator() {
    console.log('Generator started');
    
    const firstInput = yield 'Give me a number';
    console.log(`You gave me: ${firstInput}`);
    
    const secondInput = yield 'Give me another number';
    console.log(`You gave me: ${secondInput}`);
    
    return firstInput + secondInput;
}

const gen = interactiveGenerator();

console.log(gen.next().value);        // 'Give me a number'
console.log(gen.next(10).value);      // 'Give me another number'
console.log(gen.next(20));            // {value: 30, done: true}

Output:

Generator started
Give me a number
You gave me: 10
Give me another number
You gave me: 20
{value: 30, done: true}

πŸ”Ή Practical Example: ID Generator

Create a unique ID generator using generators:

// ID Generator
function* createIdGenerator(prefix = 'ID') {
    let id = 1;
    while (true) {
        yield `${prefix}-${String(id).padStart(4, '0')}`;
        id++;
    }
}

// User ID generator
const userIdGen = createIdGenerator('USER');
console.log('User IDs:');
for (let i = 0; i < 5; i++) {
    console.log(userIdGen.next().value);
}

// Product ID generator
const productIdGen = createIdGenerator('PROD');
console.log('Product IDs:');
for (let i = 0; i < 3; i++) {
    console.log(productIdGen.next().value);
}

// Order ID generator
const orderIdGen = createIdGenerator('ORD');
console.log('Order IDs:');
console.log(orderIdGen.next().value);
console.log(orderIdGen.next().value);

Output:

User IDs:
USER-0001
USER-0002
USER-0003
USER-0004
USER-0005
Product IDs:
PROD-0001
PROD-0002
PROD-0003
Order IDs:
ORD-0001
ORD-0002

πŸ”Ή Generator Benefits

Why use generators?

  • Memory Efficient: Generate values on demand
  • Lazy Evaluation: Compute only when needed
  • Infinite Sequences: Handle unlimited data
  • State Management: Maintain state between calls
  • Clean Code: Simpler than manual iterators

🧠 Test Your Knowledge

What keyword is used to pause a generator function?