时间切片通过将大任务拆分为小块并在浏览器空闲时分批执行,避免主线程阻塞;推荐使用 requestIdleCallback(需检查 deadline.timeRemaining() > 0),降级可用 setTimeout 或 Promise.then。

主线程被超大循环阻塞时,页面会卡死、无法响应点击或滚动——这不是异步问题,而是同步计算占满了 JS 主线程。时间切片(Time Slicing)不是让单次循环变快,而是把大任务拆成小块,在浏览器空闲帧中分批执行,让出控制权给渲染和事件处理。
用 requestIdleCallback 主动利用空闲时间
这是最贴近“时间切片”本意的原生方案:告诉浏览器“我有一堆工作,但只在你空闲时才做”。它会在每帧渲染完成后、且有剩余时间时触发回调,并传入一个 deadline 对象,告诉你还能安全运行多久。
- 支持情况:现代浏览器(Chrome、Edge、Firefox ≥ 55),Safari 目前不支持(需降级为 setTimeout)
- 关键点:必须检查
deadline.timeRemaining() > 0,不能硬跑固定次数;任务中途可被中断 - 示例:处理 10 万条数据,每次最多处理 500 条,再交还控制权
用 setTimeout 或 Promise.then 做简单切片(兼容兜底)
当 requestIdleCallback 不可用时,用微任务或宏任务主动让出主线程。虽然不如 idle 精准,但能有效避免卡死。
-
setTimeout(fn, 0):推到下一个宏任务队列,确保 DOM 更新和事件处理有机会执行 -
Promise.resolve().then(fn):推到当前宏任务末尾的微任务队列,延迟更短,适合轻量切片 - 技巧:每处理 N 条数据就 setTimeout 一次,N 可设为 100–1000,视单次耗时调整
切片逻辑要可中断、可恢复,别依赖全局状态
不能写 for (let i = 0; i 这种一气呵成的循环。得把任务抽象成“待处理队列 + 当前索引”,每次只处理一小段。
- 把原始数组存起来,维护一个
startIndex和batchSize - 每次切片函数只处理
data.slice(startIndex, startIndex + batchSize) - 处理完更新
startIndex,再调度下一轮 —— 这样即使用户中途操作,也能随时暂停
配合 isInputPending() 提前感知用户交互(实验性)
Chrome 98+ 支持 deadline.didTimeout 和 deadline.isInputPending(),可在切片过程中实时判断:“用户刚点了按钮吗?先停下手头活!”
- 在循环内部定期检查
if (deadline.isInputPending()) break; - 比单纯看 timeRemaining 更敏感,适合对交互响应要求极高的场景(如富文本编辑器批量格式化)
- 注意:该 API 仍为实验性,使用前需 feature detect











