生成器函数通过yield暂停恢复执行,为异步流程管理提供基础,虽被async/await取代,但在协程状态机、redux-saga等高级场景仍有价值。

JavaScript 中的生成器函数(Generator Function)本身并不直接处理异步操作,但它通过 yield 暂停和恢复执行的特性,为手动或配合工具库(如 co、redux-saga)管理异步流程提供了强大基础。现代开发中虽被 async/await 大量取代,但理解它有助于深入掌握控制流机制和某些高级场景(如协程式状态机、中间件流)。
生成器函数的基本用法:定义与执行
生成器函数用 function* 声明,返回一个迭代器对象;调用 next() 方法可逐步执行到下一个 yield 表达式,并获取其值。
示例:
function* countDown(n) {
while (n > 0) {
yield n;
n--;
}
return 'done';
}
const iterator = countDown(3);
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 'done', done: true }
用生成器模拟异步流程:手动驱动 + Promise
生成器不自动等待 Promise,但你可以手动捕获 yield 出的 Promise,在 then 中继续调用 next,从而实现“暂停等待异步结果”的效果。
立即学习“Java免费学习笔记(深入)”;
核心思路是写一个“执行器”(runner),递归处理每个 yield 返回的 Promise:
- 调用
gen.next(value)获取下一步的{value, done} - 若
value是 Promise,用.then(res => runner(res))继续执行 - 若
done为true,结束流程
简化版执行器示例:
function run(genFn) {
const gen = genFn();
function next(data) {
const result = gen.next(data);
if (result.done) return result.value;
result.value.then(next);
}
next();
}
// 使用
function* fetchUser() {
const res = yield fetch('/api/user');
const user = yield res.json();
console.log(user.name);
}
run(fetchUser);
与 async/await 对比:为什么现在少用了?
生成器+执行器的方式本质是手写协程调度器,而 async/await 是语言级支持,更简洁可靠:
-
async/await自动处理 Promise 链、错误传播(try/catch直接捕获异步错误) - 生成器需额外封装执行逻辑,出错时堆栈不直观,调试困难
-
浏览器和 Node.js 全面支持
async/await,无需 polyfill 或工具库
不过在特定框架中仍有价值:比如 redux-saga 利用生成器实现可测试、可撤销、可回溯的副作用管理,把异步逻辑从组件中抽离为声明式“saga”。
实际建议:什么情况下还该考虑生成器?
日常业务开发优先使用 async/await。仅在以下情况可考虑生成器:
- 需要精确控制执行时机(如游戏循环、动画帧协调)
- 构建类库或框架,需暴露可暂停/恢复的流程接口
- 维护旧项目中已有的
co或redux-saga代码 - 学习 JavaScript 运行时机制,理解迭代器、协程与事件循环的关系
它不是过时的技术,而是退居幕后成为底层能力——就像你不用手写红黑树,但理解它能帮你更好用 Map 和 Set。











