JavaScript的生成器函数是ES6引入的特殊函数类型,它可以通过yield关键字暂停执行,之后还能恢复继续执行,这种特性让它在状态机实现和异步流程控制场景中发挥着独特作用。

生成器函数的基本特性
生成器函数通过function*语法定义,调用后不会立即执行函数体,而是返回一个迭代器对象,通过迭代器的next方法可以控制函数执行。
function* simpleGenerator() {
console.log('第一次执行');
yield 1; // 暂停执行,返回1
console.log('第二次执行');
yield 2; // 再次暂停,返回2
console.log('第三次执行');
return 3; // 结束执行,返回3
}
const gen = simpleGenerator();
console.log(gen.next()); // 输出:第一次执行 {value: 1, done: false}
console.log(gen.next()); // 输出:第二次执行 {value: 2, done: false}
console.log(gen.next()); // 输出:第三次执行 {value: 3, done: true}
从上面的例子可以看到,每次调用next方法,生成器函数就会执行到下一个yield位置暂停,返回值由yield后面的表达式决定,同时done属性标识是否执行完毕。
生成器函数在状态机实现中的优势
状态机是处理多状态切换逻辑的常用模型,传统实现方式往往需要维护状态变量、编写大量的条件判断代码,而生成器函数可以天然适配状态机的执行逻辑。
优势一:状态与执行逻辑天然绑定
传统状态机实现通常需要单独维护状态变量,比如下面的简单状态机示例:
// 传统状态机实现
let state = 'idle';
function runStateMachine(action) {
if (state === 'idle') {
if (action === 'start') {
console.log('切换到运行状态');
state = 'running';
}
} else if (state === 'running') {
if (action === 'pause') {
console.log('切换到暂停状态');
state = 'paused';
} else if (action === 'stop') {
console.log('切换到停止状态');
state = 'stopped';
}
} else if (state === 'paused') {
if (action === 'resume') {
console.log('切换回运行状态');
state = 'running';
} else if (action === 'stop') {
console.log('切换到停止状态');
state = 'stopped';
}
}
}
runStateMachine('start'); // 切换到运行状态
runStateMachine('pause'); // 切换到暂停状态
使用生成器函数实现同样的状态机,可以把每个状态的逻辑直接放在yield位置,状态切换通过next方法的入参控制:
// 生成器实现状态机
function* stateMachine() {
console.log('当前状态:idle');
let action = yield; // 等待外部传入动作
while (true) {
if (action === 'start') {
console.log('当前状态:running');
action = yield;
} else if (action === 'pause') {
console.log('当前状态:paused');
action = yield;
} else if (action === 'resume') {
console.log('当前状态:running');
action = yield;
} else if (action === 'stop') {
console.log('当前状态:stopped');
return;
}
}
}
const sm = stateMachine();
sm.next(); // 启动状态机,输出:当前状态:idle
sm.next('start'); // 传入start动作,输出:当前状态:running
sm.next('pause'); // 传入pause动作,输出:当前状态:paused
sm.next('stop'); // 传入stop动作,输出:当前状态:stopped
可以看到生成器实现的状态机不需要单独维护状态变量,每个状态的逻辑和暂停点一一对应,代码结构更清晰,状态切换的逻辑也更直观。
优势二:避免复杂的分支嵌套
当状态机的状态较多、切换逻辑复杂时,传统实现的条件判断会层层嵌套,可读性大幅下降。生成器函数通过线性的执行流程,把不同状态的逻辑按顺序排列,避免了大量的if-else或者switch-case嵌套,降低了代码的维护成本。
优势三:支持状态执行的暂停和恢复
生成器函数天然支持暂停和恢复,这在需要等待外部事件触发状态切换的场景中非常有用,比如需要等待用户操作、定时器触发后再切换状态,不需要额外编写等待逻辑,只需要在对应的yield位置暂停即可。
生成器函数如何简化复杂异步流程控制
JavaScript中的异步操作传统上通过回调函数处理,容易出现回调地狱的问题,后续的Promise方案虽然改善了这个问题,但在处理多个串行异步、异步和同步逻辑混合的场景时,代码还是不够直观。生成器函数结合Promise可以很好地解决这个问题。
基础异步流程处理
我们可以通过生成器函数的暂停特性,把异步操作放在yield后面,在next方法中处理异步操作的结果,实现类似同步代码的异步编写方式。
// 模拟异步请求函数
function mockRequest(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`请求${url}的结果`);
}, 1000);
});
}
// 生成器函数处理异步流程
function* asyncFlow() {
console.log('开始第一个请求');
const res1 = yield mockRequest('/api/data1');
console.log('第一个请求结果:', res1);
console.log('开始第二个请求');
const res2 = yield mockRequest('/api/data2');
console.log('第二个请求结果:', res2);
return '所有请求完成';
}
// 执行生成器异步流程的辅助函数
function runGenerator(gen) {
const g = gen();
function next(data) {
const result = g.next(data);
if (result.done) {
console.log(result.value);
return;
}
// 如果yield后面是Promise,等待Promise完成后再执行next
result.value.then(next);
}
next();
}
runGenerator(asyncFlow);
// 输出顺序:
// 开始第一个请求
// 等待1秒后输出:第一个请求结果:请求/api/data1的结果
// 开始第二个请求
// 再等待1秒后输出:第二个请求结果:请求/api/data2的结果
// 所有请求完成
上面的代码中,异步流程的编写方式和同步代码几乎一致,不需要嵌套回调函数,也不需要链式调用then,逻辑更加清晰。
处理错误和异常
生成器函数还可以通过throw方法向生成器内部抛出异常,方便处理异步流程中的错误,不需要在每个Promise的catch中处理。
function* errorAsyncFlow() {
try {
const res = yield mockRequest('/api/error');
console.log('请求成功:', res);
} catch (e) {
console.log('请求失败:', e);
}
}
function runGeneratorWithError(gen) {
const g = gen();
function next(data) {
const result = g.next(data);
if (result.done) return;
result.value.then(next).catch(err => {
g.throw(err); // 向生成器内部抛出异常
});
}
next();
}
runGeneratorWithError(errorAsyncFlow);
生成器与其他异步方案的对比
现在JavaScript中已经有了async/await语法,它其实是生成器函数结合Promise的语法糖,但是生成器函数在状态机场景下的灵活性更高,因为我们可以手动控制next的调用时机,而async/await是自动执行的,不适合需要手动控制执行进度的状态机场景。
| 方案 | 适合场景 | 执行控制 |
|---|---|---|
| 回调函数 | 简单异步场景 | 被动等待回调触发 |
| Promise | 中等复杂度异步场景 | 链式调用控制流程 |
| 生成器函数+Promise | 状态机、需要手动控制执行进度的复杂异步场景 | 手动调用next控制执行 |
| async/await | 常规异步流程场景 | 自动执行,无法手动暂停 |
总结
生成器函数通过yield关键字提供的暂停和恢复执行能力,在状态机实现中可以把状态逻辑和执行流程天然绑定,避免复杂的分支嵌套,支持灵活的暂停恢复控制。在复杂异步流程控制中,结合Promise可以实现类同步的编码方式,简化异步逻辑,降低回调嵌套问题。虽然现在async/await更常用在常规异步场景,但生成器函数在需要手动控制执行状态的特殊场景下,依然是非常实用的技术方案。
JavaScript生成器函数状态机异步流程控制修改时间:2026-06-16 06:30:53