Generator函数可通过“暂停-恢复”机制将长任务分片执行,配合宏任务调度与时间切片(如5ms阈值)避免UI阻塞,适用于大数据处理等场景,并可封装调度器实现loading状态联动。

可以用 Generator 函数把长任务切分成小块,在每次执行一小段后主动让出主线程,等浏览器完成渲染或响应用户操作后再继续。核心是“暂停-恢复”机制,配合事件循环调度,避免阻塞 UI。
Generator 分片执行的基本结构
定义一个生成器函数,把原本的大循环或密集计算逻辑拆成 yield 步骤:
- 用 function* 声明生成器,内部用 yield 标记可中断点
- 每次调用 next() 执行到下一个 yield,返回 { value, done }
- 把 next() 调用放到宏任务中(如 setTimeout、requestIdleCallback),确保中间能触发渲染
结合时间切片控制单次执行时长
不能只靠 yield 次数,还得防止单次执行太久。实际中常搭配时间阈值(比如 1ms 或 5ms)动态切分:
- 在生成器内部维护一个计时起点,每处理一批数据就检查 performance.now() - start > 5
- 超时就 yield,交还控制权,下次宏任务再 resume
- 适合场景:大数据排序、树遍历、Canvas 批量绘制、JSON 解析等同步重负载
封装一个可复用的执行调度器
手动反复写 setTimeout + next() 容易出错,可以封装一个 execGenerator 工具:
- 接收生成器函数和可选参数,调用它得到迭代器
- 内部用 setTimeout(..., 0) 或 queueMicrotask(注意微任务无法让出渲染)驱动 next()
- done 为 true 时触发完成回调;出错时 try/catch 并传递给 reject
- 支持传入 abortSignal 或中断条件,便于用户取消长任务
与 loading 状态联动的实际例子
解决开头提到的“loading 不显示”问题:
- 先设 loading.style.display = 'block',然后立即调用 execGenerator(bigTaskGenerator)
- 生成器内每处理 100 条数据就 yield 一次,调度器自动安排下一轮
- 浏览器在两次宏任务之间完成样式生效、重排重绘,loading 就能立刻可见
- 最后 done 为 true 时隐藏 loading,更新结果区域











