JavaScript Generator函数深度解析:构建异步迭代器与状态机实战指南

免费资源下载
作者:前端架构师 |
发布日期:2024年1月 |
阅读时间:15分钟

一、Generator核心概念与工作原理

Generator函数是ES6引入的一种特殊函数,它能够暂停执行并在后续恢复执行。这种特性使得Generator成为处理异步操作、构建迭代器和实现状态机的强大工具。

1.1 与传统函数的区别

  • 可暂停执行:使用yield关键字暂停函数执行
  • 保持状态:暂停时保留局部变量状态
  • 双向通信:可以通过next()方法向函数内部传递值
  • 返回迭代器:调用Generator函数返回一个迭代器对象

1.2 执行流程示意图

function* generatorExample() {
    console.log('开始执行');
    const a = yield '第一次暂停';
    console.log('a:', a);
    const b = yield '第二次暂停';
    console.log('b:', b);
    return '完成';
}

// 执行流程:
// 1. 创建迭代器
// 2. 首次next()执行到第一个yield
// 3. 后续next(value)从上次暂停处继续执行
// 4. 将value传递给对应的yield表达式

二、基础语法与执行控制

2.1 基本语法结构

// 定义Generator函数
function* basicGenerator() {
    yield '第一步';
    yield '第二步';
    yield '第三步';
    return '结束';
}

// 使用示例
const iterator = basicGenerator();

console.log(iterator.next()); // { value: '第一步', done: false }
console.log(iterator.next()); // { value: '第二步', done: false }
console.log(iterator.next()); // { value: '第三步', done: false }
console.log(iterator.next()); // { value: '结束', done: true }

2.2 双向通信机制

function* twoWayCommunication() {
    const name = yield '请输入你的名字:';
    const age = yield `你好 ${name},请输入你的年龄:`;
    const hobby = yield `${name},${age}岁,请输入你的爱好:`;
    
    return {
        name,
        age: parseInt(age),
        hobby,
        summary: `${name}是一名${age}岁的${hobby}爱好者`
    };
}

// 模拟用户输入
const chat = twoWayCommunication();

console.log(chat.next().value); // "请输入你的名字:"
console.log(chat.next('张三').value); // "你好 张三,请输入你的年龄:"
console.log(chat.next('25').value); // "张三,25岁,请输入你的爱好:"
const result = chat.next('编程');
console.log(result.value); // 返回完整对象

2.3 错误处理与throw方法

function* errorHandlingGenerator() {
    try {
        const x = yield '开始计算';
        const y = yield '继续计算';
        if (y === 0) {
            throw new Error('除数不能为零');
        }
        return x / y;
    } catch (error) {
        yield `捕获错误:${error.message}`;
        return null;
    }
}

const calc = errorHandlingGenerator();
calc.next(); // 启动
calc.next(10); // 设置x=10
const errorResult = calc.throw(new Error('自定义错误'));
console.log(errorResult.value); // "捕获错误:自定义错误"

三、实战:构建智能分页数据加载器

我们将创建一个支持自动重试、缓存和并发控制的智能分页数据加载器。

3.1 基础分页加载器实现

function* createPaginationLoader(apiEndpoint, options = {}) {
    const {
        pageSize = 20,
        maxRetries = 3,
        cache = new Map(),
        concurrentLimit = 3
    } = options;
    
    let currentPage = 0;
    let totalPages = null;
    const pendingRequests = new Set();
    
    while (true) {
        // 检查并发限制
        if (pendingRequests.size >= concurrentLimit) {
            yield { type: 'waiting', message: '达到并发限制,等待中...' };
            continue;
        }
        
        // 检查是否已加载所有数据
        if (totalPages !== null && currentPage >= totalPages) {
            return { type: 'complete', message: '所有数据加载完成' };
        }
        
        const pageToLoad = currentPage++;
        
        // 检查缓存
        const cacheKey = `${apiEndpoint}_page_${pageToLoad}`;
        if (cache.has(cacheKey)) {
            const cachedData = cache.get(cacheKey);
            yield { 
                type: 'cached', 
                page: pageToLoad, 
                data: cachedData 
            };
            continue;
        }
        
        // 发起请求
        let retryCount = 0;
        let success = false;
        let data = null;
        
        const requestId = Symbol(`page_${pageToLoad}`);
        pendingRequests.add(requestId);
        
        while (retryCount < maxRetries && !success) {
            try {
                yield { 
                    type: 'loading', 
                    page: pageToLoad, 
                    retry: retryCount + 1 
                };
                
                // 模拟API请求
                const response = await fetch(
                    `${apiEndpoint}?page=${pageToLoad}&size=${pageSize}`
                );
                
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}`);
                }
                
                data = await response.json();
                
                // 更新总页数
                if (totalPages === null && data.totalPages) {
                    totalPages = data.totalPages;
                }
                
                // 缓存数据
                cache.set(cacheKey, data.items);
                
                success = true;
                pendingRequests.delete(requestId);
                
                yield { 
                    type: 'success', 
                    page: pageToLoad, 
                    data: data.items,
                    hasMore: currentPage < totalPages
                };
                
            } catch (error) {
                retryCount++;
                if (retryCount === maxRetries) {
                    pendingRequests.delete(requestId);
                    yield { 
                        type: 'error', 
                        page: pageToLoad, 
                        error: error.message,
                        retries: maxRetries
                    };
                }
            }
        }
    }
}

3.2 使用示例与控制器

class PaginationController {
    constructor(loaderGenerator) {
        this.loader = loaderGenerator;
        this.iterator = null;
        this.isRunning = false;
        this.results = [];
        this.listeners = {
            data: [],
            error: [],
            complete: []
        };
    }
    
    start() {
        if (this.isRunning) return;
        
        this.isRunning = true;
        this.iterator = this.loader();
        
        const processNext = async () => {
            if (!this.isRunning) return;
            
            const result = this.iterator.next();
            
            if (result.done) {
                this.isRunning = false;
                this.emit('complete', result.value);
                return;
            }
            
            const value = await result.value;
            
            switch (value.type) {
                case 'success':
                case 'cached':
                    this.results.push(...value.data);
                    this.emit('data', {
                        page: value.page,
                        data: value.data,
                        allResults: this.results,
                        hasMore: value.hasMore
                    });
                    break;
                    
                case 'error':
                    this.emit('error', value);
                    break;
                    
                case 'waiting':
                    // 等待一段时间后继续
                    setTimeout(processNext, 1000);
                    return;
            }
            
            // 继续处理下一项
            processNext();
        };
        
        processNext();
    }
    
    stop() {
        this.isRunning = false;
        if (this.iterator && this.iterator.return) {
            this.iterator.return();
        }
    }
    
    emit(event, data) {
        if (this.listeners[event]) {
            this.listeners[event].forEach(listener => listener(data));
        }
    }
    
    on(event, callback) {
        if (this.listeners[event]) {
            this.listeners[event].push(callback);
        }
        return this;
    }
}

// 使用示例
const controller = new PaginationController(
    () => createPaginationLoader('/api/products', {
        pageSize: 10,
        maxRetries: 2,
        concurrentLimit: 2
    })
);

controller
    .on('data', (data) => {
        console.log(`收到第${data.page}页数据,共${data.data.length}条`);
        console.log('累计数据:', data.allResults.length);
    })
    .on('error', (error) => {
        console.error(`页面${error.page}加载失败:`, error.error);
    })
    .on('complete', () => {
        console.log('分页加载完成,总计:', controller.results.length);
    });

controller.start();

四、高级应用:有限状态机实现

使用Generator实现一个可配置、可扩展的有限状态机(FSM)。

4.1 状态机核心实现

function* createStateMachine(config) {
    const {
        initialState,
        states,
        transitions,
        onTransition,
        onError
    } = config;
    
    let currentState = initialState;
    const stateHistory = [initialState];
    
    // 验证状态转移
    function validateTransition(fromState, toState, event) {
        const transition = transitions.find(t => 
            t.from === fromState && 
            t.event === event
        );
        
        if (!transition) {
            throw new Error(`从状态 ${fromState} 无法通过事件 ${event} 转移到 ${toState}`);
        }
        
        if (!transition.to.includes(toState)) {
            throw new Error(`事件 ${event} 不允许转移到状态 ${toState}`);
        }
        
        return transition;
    }
    
    // 处理状态转移
    function* transitionTo(newState, event, data = {}) {
        const oldState = currentState;
        
        try {
            // 执行离开动作
            if (states[oldState] && states[oldState].onExit) {
                yield* states[oldState].onExit(newState, data);
            }
            
            // 验证转移
            const transition = validateTransition(oldState, newState, event);
            
            // 执行转移动作
            if (transition.action) {
                yield* transition.action(data);
            }
            
            // 更新状态
            currentState = newState;
            stateHistory.push(newState);
            
            // 执行进入动作
            if (states[newState] && states[newState].onEnter) {
                yield* states[newState].onEnter(oldState, data);
            }
            
            // 回调通知
            if (onTransition) {
                onTransition({
                    from: oldState,
                    to: newState,
                    event,
                    data,
                    timestamp: Date.now()
                });
            }
            
            return { success: true, newState, oldState };
            
        } catch (error) {
            if (onError) {
                onError(error, { from: oldState, to: newState, event, data });
            }
            return { success: false, error: error.message };
        }
    }
    
    // 主循环
    while (true) {
        const { event, toState, data } = yield {
            state: currentState,
            history: [...stateHistory],
            availableTransitions: transitions
                .filter(t => t.from === currentState)
                .map(t => t.event)
        };
        
        if (event === 'RESET') {
            currentState = initialState;
            stateHistory.length = 0;
            stateHistory.push(initialState);
            continue;
        }
        
        if (event === 'GET_HISTORY') {
            yield { type: 'history', history: [...stateHistory] };
            continue;
        }
        
        const result = yield* transitionTo(toState, event, data);
        yield { type: 'transition_result', result };
    }
}

4.2 订单状态机示例

// 定义订单状态机配置
const orderStateMachine = () => createStateMachine({
    initialState: 'draft',
    
    states: {
        draft: {
            onEnter: function*(fromState, data) {
                console.log('订单创建:', data.orderId);
                yield { type: 'log', message: '订单草稿创建' };
            }
        },
        paid: {
            onEnter: function*(fromState, data) {
                console.log('订单已支付:', data.amount);
                yield { type: 'notify', message: '支付成功通知' };
            }
        },
        shipped: {
            onEnter: function*(fromState, data) {
                console.log('订单已发货:', data.trackingNumber);
                yield { type: 'log', message: '发货记录' };
            },
            onExit: function*(toState, data) {
                if (toState === 'cancelled') {
                    yield { type: 'refund', message: '发货后取消需要退款' };
                }
            }
        },
        delivered: {
            onEnter: function*(fromState, data) {
                console.log('订单已送达');
                yield { type: 'notify', message: '送达通知' };
            }
        },
        cancelled: {
            onEnter: function*(fromState, data) {
                console.log('订单已取消:', data.reason);
                yield { type: 'log', message: '取消原因记录' };
            }
        }
    },
    
    transitions: [
        { from: 'draft', event: 'submit', to: ['paid'] },
        { from: 'draft', event: 'cancel', to: ['cancelled'] },
        { from: 'paid', event: 'ship', to: ['shipped'] },
        { from: 'paid', event: 'cancel', to: ['cancelled'] },
        { from: 'shipped', event: 'deliver', to: ['delivered'] },
        { from: 'shipped', event: 'cancel', to: ['cancelled'] },
        { from: 'delivered', event: 'return', to: ['cancelled'] }
    ],
    
    onTransition: (info) => {
        console.log(`状态转移: ${info.from} -> ${info.to} [${info.event}]`);
    },
    
    onError: (error, context) => {
        console.error('状态转移错误:', error.message, context);
    }
});

// 使用状态机
async function processOrder() {
    const orderFSM = orderStateMachine();
    const iterator = orderFSM.next(); // 初始化
    
    // 提交订单
    iterator.next({ event: 'submit', toState: 'paid', data: { orderId: '123', amount: 100 } });
    
    // 发货
    iterator.next({ event: 'ship', toState: 'shipped', data: { trackingNumber: 'TRACK123' } });
    
    // 尝试非法转移(应该失败)
    try {
        iterator.next({ event: 'cancel', toState: 'cancelled', data: { reason: '用户取消' } });
    } catch (error) {
        console.log('预期中的错误:', error.message);
    }
    
    // 送达
    iterator.next({ event: 'deliver', toState: 'delivered', data: {} });
    
    // 获取历史
    const historyReq = iterator.next({ event: 'GET_HISTORY' });
    console.log('状态历史:', historyReq.value.history);
}

五、Generator与异步编程的完美结合

5.1 实现async/await的底层机制

function asyncRunner(generatorFunc) {
    return function(...args) {
        const iterator = generatorFunc.apply(this, args);
        
        return new Promise((resolve, reject) => {
            function step(nextFn, arg) {
                let result;
                try {
                    result = iterator[nextFn](arg);
                } catch (error) {
                    reject(error);
                    return;
                }
                
                const { value, done } = result;
                
                if (done) {
                    resolve(value);
                    return;
                }
                
                // 处理不同类型的值
                Promise.resolve(value).then(
                    (v) => step('next', v),
                    (e) => step('throw', e)
                );
            }
            
            step('next');
        });
    };
}

// 使用示例
const asyncFetchData = asyncRunner(function* () {
    try {
        console.log('开始获取用户数据...');
        const userResponse = yield fetch('/api/user');
        const user = yield userResponse.json();
        
        console.log('开始获取订单数据...');
        const ordersResponse = yield fetch(`/api/orders/${user.id}`);
        const orders = yield ordersResponse.json();
        
        console.log('开始获取商品数据...');
        const productsResponse = yield fetch('/api/products');
        const products = yield productsResponse.json();
        
        return {
            user,
            orders,
            products,
            summary: `用户${user.name}有${orders.length}个订单,${products.length}个商品`
        };
    } catch (error) {
        console.error('数据获取失败:', error);
        throw error;
    }
});

// 调用方式与async/await相同
asyncFetchData().then(result => {
    console.log('最终结果:', result);
}).catch(error => {
    console.error('错误:', error);
});

5.2 并发任务调度器

function* createTaskScheduler(maxConcurrent = 3) {
    const taskQueue = [];
    const activeTasks = new Set();
    let taskId = 0;
    
    function* executeTask(task) {
        const id = ++taskId;
        activeTasks.add(id);
        
        try {
            yield { type: 'task_start', id, task: task.name };
            const result = yield* task();
            yield { type: 'task_complete', id, result };
            return result;
        } catch (error) {
            yield { type: 'task_error', id, error };
            throw error;
        } finally {
            activeTasks.delete(id);
        }
    }
    
    function* scheduleTask(task, priority = 0) {
        const taskWithPriority = { task, priority, id: ++taskId };
        taskQueue.push(taskWithPriority);
        taskQueue.sort((a, b) => b.priority - a.priority);
        
        yield { type: 'task_queued', task: task.name, priority };
        
        // 等待执行
        while (true) {
            if (activeTasks.size  scheduleTask(task, priority),
            stats: {
                active: activeTasks.size,
                queued: taskQueue.length,
                completed: taskId - activeTasks.size - taskQueue.length
            }
        };
        
        if (request.task && typeof request.task === 'function') {
            yield* scheduleTask(request.task, request.priority || 0);
        }
    }
}

// 使用示例
async function demoTaskScheduler() {
    const scheduler = createTaskScheduler(2);
    const iterator = scheduler.next();
    
    // 定义任务
    const tasks = [
        { name: '处理图片', fn: function*() { 
            yield new Promise(resolve => setTimeout(resolve, 1000));
            return '图片处理完成'; 
        }},
        { name: '发送邮件', fn: function*() { 
            yield new Promise(resolve => setTimeout(resolve, 500));
            return '邮件发送成功'; 
        }},
        { name: '备份数据', fn: function*() { 
            yield new Promise(resolve => setTimeout(resolve, 2000));
            return '数据备份完成'; 
        }},
        { name: '生成报告', fn: function*() { 
            yield new Promise(resolve => setTimeout(resolve, 800));
            return '报告生成完成'; 
        }}
    ];
    
    // 提交任务
    const results = await Promise.all(
        tasks.map((task, index) => {
            const request = iterator.next({
                task: task.fn,
                priority: index
            });
            
            // 处理生成器的返回结果
            return new Promise((resolve) => {
                function processResult(result) {
                    if (result.done) {
                        resolve(result.value);
                    } else {
                        const value = result.value;
                        if (value && value.type === 'task_complete') {
                            resolve(value.result);
                        } else {
                            // 继续执行
                            const next = iterator.next();
                            processResult(next);
                        }
                    }
                }
                processResult(request);
            });
        })
    );
    
    console.log('所有任务完成:', results);
}

六、性能优化与最佳实践

6.1 内存管理策略

  • 及时清理迭代器:使用return()方法显式结束Generator
  • 避免内存泄漏:确保迭代器在不再需要时被垃圾回收
  • 限制状态历史:对于长时间运行的状态机,限制历史记录大小
// 安全使用Generator的模板
function safeGeneratorUsage(generatorFunc) {
    let iterator = null;
    let isActive = true;
    
    return {
        start: function(...args) {
            if (iterator) return;
            
            iterator = generatorFunc(...args);
            isActive = true;
            
            const controller = {
                next: (value) => {
                    if (!isActive || !iterator) return { done: true };
                    return iterator.next(value);
                },
                throw: (error) => {
                    if (!isActive || !iterator) throw error;
                    return iterator.throw(error);
                },
                return: (value) => {
                    isActive = false;
                    if (iterator) {
                        const result = iterator.return(value);
                        iterator = null;
                        return result;
                    }
                    return { value, done: true };
                },
                isActive: () => isActive
            };
            
            return controller;
        },
        
        cleanup: function() {
            if (iterator && iterator.return) {
                iterator.return();
            }
            iterator = null;
            isActive = false;
        }
    };
}

6.2 性能监控与调试

function createInstrumentedGenerator(originalGenerator) {
    return function* (...args) {
        const startTime = performance.now();
        let yieldCount = 0;
        let pauseDuration = 0;
        let lastPauseTime = null;
        
        const instrumentedIterator = originalGenerator(...args);
        
        while (true) {
            if (lastPauseTime) {
                pauseDuration += performance.now() - lastPauseTime;
                lastPauseTime = null;
            }
            
            const startStep = performance.now();
            const result = instrumentedIterator.next();
            const stepDuration = performance.now() - startStep;
            
            if (result.done) {
                const totalTime = performance.now() - startTime;
                console.log({
                    type: 'generator_complete',
                    totalTime,
                    yieldCount,
                    pauseDuration,
                    efficiency: (totalTime - pauseDuration) / totalTime
                });
                return result.value;
            }
            
            yieldCount++;
            lastPauseTime = performance.now();
            
            // 添加性能数据到返回值
            const instrumentedValue = {
                ...result.value,
                _performance: {
                    yieldCount,
                    stepDuration,
                    totalElapsed: performance.now() - startTime
                }
            };
            
            yield instrumentedValue;
        }
    };
}

6.3 最佳实践总结

  1. 合理使用yield:避免在循环中过度使用yield,影响性能
  2. 错误处理:始终使用try-catch包装yield表达式
  3. 资源清理:确保在Generator完成或出错时释放资源
  4. 避免阻塞:长时间运行的Generator应考虑分片执行
  5. 类型安全:使用TypeScript增强Generator的类型检查

// 页面交互功能
document.addEventListener(‘DOMContentLoaded’, function() {
// 代码块行号显示
const codeBlocks = document.querySelectorAll(‘pre’);
codeBlocks.forEach(pre => {
const code = pre.querySelector(‘code’);
if (!code) return;

const lines = code.textContent.split(‘n’).length;
const lineNumbers = document.createElement(‘div’);
lineNumbers.innerHTML = Array.from({length: lines}, (_, i) =>
`${i + 1}`
).join(‘n’);

lineNumbers.style.cssText = `
position: absolute;
left: 0;
top: 0;
bottom: 0;
padding: 1em 0.5em;
background: #f5f5f5;
border-right: 1px solid #ddd;
text-align: right;
color: #666;
font-family: monospace;
line-height: 1.5;
user-select: none;
`;

pre.style.position = ‘relative’;
pre.style.paddingLeft = ‘3.5em’;
pre.appendChild(lineNumbers);

// 代码复制功能
const copyBtn = document.createElement(‘button’);
copyBtn.textContent = ‘复制’;
copyBtn.style.cssText = `
position: absolute;
right: 10px;
top: 10px;
background: #007bff;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
z-index: 10;
`;

copyBtn.addEventListener(‘click’, async () => {
try {
await navigator.clipboard.writeText(code.textContent);
copyBtn.textContent = ‘已复制!’;
setTimeout(() => {
copyBtn.textContent = ‘复制’;
}, 2000);
} catch (err) {
console.error(‘复制失败:’, err);
}
});

pre.appendChild(copyBtn);
});

// 目录导航高亮
const sections = document.querySelectorAll(‘section[id]’);
const navLinks = document.querySelectorAll(‘nav a’);

function highlightNav() {
let currentSection = ”;
const scrollPos = window.scrollY + 100;

sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;

if (scrollPos >= sectionTop && scrollPos {
link.style.fontWeight = ‘normal’;
link.style.color = ”;

if (link.getAttribute(‘href’) === `#${currentSection}`) {
link.style.fontWeight = ‘bold’;
link.style.color = ‘#007bff’;
}
});
}

window.addEventListener(‘scroll’, highlightNav);
highlightNav();

// 语法高亮(简化版)
function highlightSyntax() {
const codeElements = document.querySelectorAll(‘pre code’);

codeElements.forEach(code => {
let html = code.innerHTML;

// 关键字高亮
const keywords = [
‘function’, ‘yield’, ‘return’, ‘const’, ‘let’, ‘var’,
‘if’, ‘else’, ‘try’, ‘catch’, ‘throw’, ‘new’,
‘class’, ‘extends’, ‘async’, ‘await’, ‘for’, ‘while’,
‘switch’, ‘case’, ‘break’, ‘continue’, ‘default’
];

keywords.forEach(keyword => {
const regex = new RegExp(`\b${keyword}\b`, ‘g’);
html = html.replace(regex, `${keyword}`);
});

// 字符串高亮
html = html.replace(/(‘.*?’|”.*?”)/g, ‘$1‘);

// 注释高亮
html = html.replace(/(//.*)/g, ‘$1‘);

// 数字高亮
html = html.replace(/b(d+)b/g, ‘$1‘);

code.innerHTML = html;
});
}

highlightSyntax();
});

JavaScript Generator函数深度解析:构建异步迭代器与状态机实战指南
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 javascript JavaScript Generator函数深度解析:构建异步迭代器与状态机实战指南 https://www.taomawang.com/web/javascript/1520.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务