Skip to main content
Course/Module 6/Topic 3 of 3Advanced

JavaScript Generators & Iterators

Learn about generators, iterators, and how they can be used for advanced asynchronous programming patterns and custom iteration

45 minBy Priygop TeamLast updated: Feb 2026

Generator Functions

Generators are functions that can be paused and resumed, allowing for more complex control flow and the creation of custom iterators. They're particularly useful for handling asynchronous operations and creating infinite sequences.

Basic Generators

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

const gen = numberGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().done); // true

// Generator with parameters
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);
}

// Generator with return
function* generatorWithReturn() {
    yield 1;
    yield 2;
    return 'finished';
    yield 3; // This won't execute
}

const gen2 = generatorWithReturn();
console.log(gen2.next()); // { value: 1, done: false }
console.log(gen2.next()); // { value: 2, done: false }
console.log(gen2.next()); // { value: 'finished', done: true }

// Generator with throw
function* generatorWithError() {
    try {
        yield 1;
        yield 2;
    } catch (error) {
        console.log('Caught error:', error.message);
        yield 3;
    }
}

const gen3 = generatorWithError();
console.log(gen3.next().value); // 1
console.log(gen3.throw(new Error('Something went wrong')).value); // 3

// Custom iterator with generator
const customIterable = {
    *[Symbol.iterator]() {
        yield 'first';
        yield 'second';
        yield 'third';
    }
};

for (const item of customIterable) {
    console.log(item);
}

// Generator for data processing
function* dataProcessor(data) {
    for (const item of data) {
        if (item > 0) {
            yield item * 2;
        }
    }
}

const numbers = [1, -2, 3, -4, 5];
const processed = Array.from(dataProcessor(numbers));
console.log(processed); // [2, 6, 10]

Async Generators

Example
// Async generator
async function* asyncNumberGenerator() {
    for (let i = 1; i <= 5; i++) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        yield i;
    }
}

async function consumeAsyncGenerator() {
    for await (const num of asyncNumberGenerator()) {
        console.log(num);
    }
}

// Async generator for API pagination
async function* fetchPaginatedData(url) {
    let page = 1;
    let hasMore = true;
    
    while (hasMore) {
        try {
            const response = await fetch(`${url}?page=${page}`);
            const data = await response.json();
            
            if (data.items && data.items.length > 0) {
                yield* data.items;
                page++;
            } else {
                hasMore = false;
            }
        } catch (error) {
            console.error('Error fetching data:', error);
            hasMore = false;
        }
    }
}

// Async generator for file reading
async function* readFileLines(filePath) {
    const fileHandle = await fs.open(filePath, 'r');
    const reader = fileHandle.createReadStream();
    
    for await (const chunk of reader) {
        const lines = chunk.toString().split('\n');
        for (const line of lines) {
            if (line.trim()) {
                yield line.trim();
            }
        }
    }
    
    await fileHandle.close();
}

// Async generator with error handling
async function* robustDataFetcher(urls) {
    for (const url of urls) {
        try {
            const response = await fetch(url);
            if (response.ok) {
                const data = await response.json();
                yield { url, data, success: true };
            } else {
                yield { url, error: `HTTP ${response.status}`, success: false };
            }
        } catch (error) {
            yield { url, error: error.message, success: false };
        }
    }
}

// Generator for state management
function* stateMachine() {
    let state = 'idle';
    
    while (true) {
        const action = yield state;
        
        switch (action) {
            case 'start':
                state = 'running';
                break;
            case 'pause':
                state = 'paused';
                break;
            case 'stop':
                state = 'stopped';
                break;
            case 'reset':
                state = 'idle';
                break;
        }
    }
}

// Test async generators
async function testAsyncGenerators() {
    console.log('Starting async generator test...');
    
    for await (const num of asyncNumberGenerator()) {
        console.log(`Received: ${num}`);
    }
    
    console.log('Async generator test completed');
}

// Test state machine
const machine = stateMachine();
console.log(machine.next().value); // 'idle'
console.log(machine.next('start').value); // 'running'
console.log(machine.next('pause').value); // 'paused'
console.log(machine.next('stop').value); // 'stopped'

Practice Exercise: Generators

Example
// Exercise: Build a Generator-based Pipeline
class DataPipeline {
    constructor() {
        this.stages = [];
    }
    
    addStage(stage) {
        this.stages.push(stage);
        return this;
    }
    
    *process(data) {
        let result = data;
        
        for (const stage of this.stages) {
            if (typeof stage === 'function') {
                result = stage(result);
            } else if (stage && typeof stage.process === 'function') {
                result = stage.process(result);
            }
            
            yield result;
        }
    }
    
    async *processAsync(data) {
        let result = data;
        
        for (const stage of this.stages) {
            if (typeof stage === 'function') {
                result = await stage(result);
            } else if (stage && typeof stage.processAsync === 'function') {
                result = await stage.processAsync(result);
            }
            
            yield result;
        }
    }
}

// Exercise: Build a Generator-based Event Emitter
class GeneratorEventEmitter {
    constructor() {
        this.listeners = new Map();
        this.generators = new Map();
    }
    
    on(event, generator) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        
        this.listeners.get(event).push(generator);
    }
    
    emit(event, data) {
        const eventGenerators = this.listeners.get(event) || [];
        
        eventGenerators.forEach(generator => {
            if (typeof generator === 'function') {
                generator(data);
            } else if (generator && typeof generator.next === 'function') {
                generator.next(data);
            }
        });
    }
    
    *createListener(event) {
        while (true) {
            const data = yield;
            console.log(`Event ${event} received:`, data);
        }
    }
}

// Exercise: Build a Generator-based State Manager
class GeneratorStateManager {
    constructor(initialState = {}) {
        this.state = { ...initialState };
        this.history = [];
        this.generators = new Map();
    }
    
    *createStateGenerator() {
        while (true) {
            const action = yield this.state;
            
            if (action.type === 'UPDATE') {
                this.state = { ...this.state, ...action.payload };
                this.history.push({ ...this.state });
            } else if (action.type === 'RESET') {
                this.state = { ...this.initialState };
                this.history = [];
            } else if (action.type === 'UNDO') {
                if (this.history.length > 0) {
                    this.history.pop();
                    this.state = this.history.length > 0 
                        ? this.history[this.history.length - 1] 
                        : { ...this.initialState };
                }
            }
        }
    }
    
    update(payload) {
        const generator = this.generators.get('main');
        if (generator) {
            generator.next({ type: 'UPDATE', payload });
        }
    }
    
    reset() {
        const generator = this.generators.get('main');
        if (generator) {
            generator.next({ type: 'RESET' });
        }
    }
    
    undo() {
        const generator = this.generators.get('main');
        if (generator) {
            generator.next({ type: 'UNDO' });
        }
    }
    
    getState() {
        return { ...this.state };
    }
    
    getHistory() {
        return [...this.history];
    }
}

// Test the exercises
const pipeline = new DataPipeline()
    .addStage(data => data.filter(x => x > 0))
    .addStage(data => data.map(x => x * 2))
    .addStage(data => data.reduce((sum, x) => sum + x, 0));

const data = [-1, 2, -3, 4, -5, 6];
const results = Array.from(pipeline.process(data));
console.log('Pipeline results:', results);

const emitter = new GeneratorEventEmitter();
const listener = emitter.createListener('user-action');
emitter.on('user-action', listener);

emitter.emit('user-action', { type: 'click', target: 'button' });

const stateManager = new GeneratorStateManager({ count: 0, user: null });
const stateGen = stateManager.createStateGenerator();
stateManager.generators.set('main', stateGen);

stateManager.update({ count: 1 });
stateManager.update({ user: { name: 'John' } });
console.log('Current state:', stateManager.getState());

stateManager.undo();
console.log('After undo:', stateManager.getState());

Try It Yourself — Asynchronous JavaScript

Try It Yourself — Asynchronous JavaScriptHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|40 lines|1814 chars|✓ Valid syntax
UTF-8

Quick Quiz — Asynchronous JavaScript

Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep