发布日期: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 最佳实践总结
- 合理使用yield:避免在循环中过度使用yield,影响性能
- 错误处理:始终使用try-catch包装yield表达式
- 资源清理:确保在Generator完成或出错时释放资源
- 避免阻塞:长时间运行的Generator应考虑分片执行
- 类型安全:使用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();
});

