javascript中生成器函数如何使用_它如何简化异步流程

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

JavaScript 中的生成器函数(Generator Function)本身并不直接处理异步操作,但它通过 yield 暂停和恢复执行的特性,为手动或配合工具库(如 coredux-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,从而实现“暂停等待异步结果”的效果。

核心思路是写一个“执行器”(runner),递归处理每个 yield 返回的 Promise:

  • 调用 gen.next(value) 获取下一步的 {value, done}
  • value 是 Promise,用 .then(res => runner(res)) 继续执行
  • donetrue,结束流程

简化版执行器示例:

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。仅在以下情况可考虑生成器:

  • 需要精确控制执行时机(如游戏循环、动画帧协调)
  • 构建类库或框架,需暴露可暂停/恢复的流程接口
  • 维护旧项目中已有的 coredux-saga 代码
  • 学习 JavaScript 运行时机制,理解迭代器、协程与事件循环的关系

它不是过时的技术,而是退居幕后成为底层能力——就像你不用手写红黑树,但理解它能帮你更好用 Map 和 Set。