在微任务中直接调用 next() 启动或恢复 Generator 易致调用栈越界,因 Generator 恢复与微任务调度形成隐式同步递归链;正确做法是避免裸调 next()、用 Promise 封装单次推进、主动让出控制权。

在微任务(如 Promise.then、queueMicrotask)中直接调用 next() 启动或恢复 Generator,容易引发调用栈越界(RangeError: Maximum call stack size exceeded),根本原因不是微任务本身有问题,而是 Generator 的恢复执行与微任务调度形成了隐式的同步递归链——尤其当 Generator 内部又触发新的微任务并再次调用 next() 时。
Generator 恢复执行不等于异步解耦
很多人误以为“把 gen.next() 放进 queueMicrotask 就安全了”,但事实是:如果 Generator 函数体内部含有 yield 后立刻又调用自身(比如手动循环驱动),而每次 next() 都在微任务中同步触发下一次,就等效于:
- 微任务 A 执行
gen.next()→ yield 暂停 - Generator 内部立即
queueMicrotask(() => gen.next()) - 微任务 B 执行 → 又触发 C、C 触发 D……
- 虽然没有传统函数调用栈压入,但 V8 等引擎对连续微任务链仍有限制(尤其配合闭包上下文保存时),最终触发引擎级栈深限制
正确驱动 Generator 的三原则
要让 Generator 在异步环境中稳定运行,必须切断隐式递归链,明确控制权移交节奏:
-
不裸调 next():永远不要在
yield后直接写gen.next()或在微任务里无条件调用;改用状态机显式管理当前是否可推进 -
用 Promise 封装单次推进:每个
next()应返回一个 Promise,并在done === true或出错时 resolve/reject,避免连续 microtask 堆叠 -
主动让出控制权:在关键节点(如循环入口、重试逻辑)插入
await new Promise(resolve => setTimeout(resolve, 0))或await Promise.resolve(),强制中断当前微任务流,清空调用上下文
典型错误写法 vs 安全重构示例
危险写法(极易爆栈):
async function











