什么是javascript的生成器函数_如何用它处理异步?

12次阅读

生成器函数是可暂停执行的函数,用function*声明、yield暂停并产出值,调用后返回迭代器,需手动.next()推进;yield不自动等待promise,需外部runner配合await;async/await已取代其大部分用途。

什么是javascript的生成器函数_如何用它处理异步?

生成器函数是什么?function*yield 的真实作用

生成器函数不是异步语法糖,它本质是**可暂停执行的函数**,靠 function* 声明、用 yield 暂停并产出值。调用后返回一个迭代器对象,必须手动调用 .next() 才会继续执行到下一个 yield 或函数结束。

它不自动处理异步,但提供了控制流“断点”的能力——这正是配合异步操作的关键基础。

  • yield 后面的表达式会立即求值,但函数执行权交还给调用方,后续需再次 .next() 才继续
  • 生成器内部的 return 会令迭代器的 done: true,且 value 为返回值
  • 不能用 async function* 直接让生成器变成“异步生成器”,那是另一套机制(AsyncIterator

如何用 yield 暂停并等待 Promise 完成?

直接 yield fetch('/api') 不会等请求完成,只是把 Promise 对象当普通值产出。真正要等它,得靠外部协程驱动器(co 函数或手写 runner)来捕获 yield 出的 Promise 并 await 它。

下面是一个最小可行 runner 示例:

立即学习Java免费学习笔记(深入)”;

function run(genFn) {   const gen = genFn();   function next(data) {     const { value, done } = gen.next(data);     if (done) return value;     return Promise.resolve(value).then(next);   }   return next(); }

使用时:

function* apiFlow() {   const res = yield fetch('/user');   const user = yield res.json();   yield console.log(user.name); } run(apiFlow); // 自动 await 每个 yield 出的 Promise
  • runner 必须递归调用 .next(),且对每个 valuePromise.resolve().then()
  • 如果 yield 出的不是 Promise,Promise.resolve() 会原样包裹,不影响流程
  • 错误需在 runner 内用 gen.throw() 传递,否则会被静默吞掉

async/await 普及后,还有必要手写生成器驱动吗?

基本没必要。现代 javaScript 中 async/await 已覆盖全部常见异步场景,语法更直白,调试更友好,V8 引擎优化也更充分。

生成器驱动方案只在极少数场景仍有价值:

  • 需要精确控制每一步暂停/恢复时机(如实现自定义状态机、协程调度)
  • 兼容非常老的运行时(IE11 或无 async 支持的嵌入 JS 引擎),且无法引入 Babel 转译
  • 已有基于 co 的旧代码库,迁移成本过高

注意:async function* 是“异步生成器函数”,返回 AsyncIterator,和用 yield 驱动 Promise 完全不同——它用于 for await...of 流式消费异步数据源(如 SSE、数据库游标),不是替代 async/await 的方式。

容易被忽略的坑:生成器的状态不可重用

生成器实例(iterator)是一次性的。一旦执行到 done: true,再调用 .next() 永远返回 { value: undefined, done: true };也不会自动重置。

  • 每次需要新流程,必须重新调用生成器函数(genFn())创建新迭代器
  • 闭包变量不会重置,但生成器函数体内的局部变量每次调用都是全新的
  • 不要试图把生成器当作类方法反复调用同一个实例——这是最常见的误用

异步流程中若需多次触发同一逻辑,应封装为函数返回新生成器,而非复用旧 iterator。

text=ZqhQzanResources