捕获宏任务间执行间隙的本质是在宏任务切换窗口主动插入标记点,而非监听空闲;可通过 setTimeout(0)、requestAnimationFrame 或 PerformanceObserver 实现,但需区分同步代码与真实间隙。

捕获宏任务间的执行间隙,本质不是“监听空闲”,而是主动在间隙处插入可追踪的标记点。JavaScript 的事件循环本身不暴露“空闲”状态,但每个宏任务结束后、下一个宏任务开始前,存在一个确定的调度窗口——这个窗口里微任务已清空、渲染可能触发、下个宏任务尚未启动。利用这个节奏,就能实现可观测性。
用 setTimeout(0) 插入间隙探测点
这是最直接、兼容性最好的方式。把监控逻辑包装进 setTimeout(fn, 0),它会被推入宏任务队列尾部,确保在当前宏任务(含所有后续微任务)完成后才执行。
- 适合记录“上一任务结束 → 下一任务开始”之间的间隔,例如:
console.timeEnd('last-task-gap') - 注意:浏览器对
setTimeout(0)有 ≥4ms 的最小延迟,实际时间反映的是调度+系统负载,而非纯 JS 执行耗时 - 多次连续使用时,不要依赖它们的绝对顺序;可用唯一 ID 或时间戳打标,避免混淆
结合 requestAnimationFrame 捕获渲染前间隙
如果关注的是 UI 更新相关的间隙(如动画帧之间),requestAnimationFrame 更精准。它在浏览器下一次重绘前调用,天然位于宏任务结束、渲染开始前的节点。
- 可用于测量“状态更新 → 视图呈现”的延迟,例如:
rafStart = performance.now(); requestAnimationFrame(() => console.log('render latency:', performance.now() - rafStart)) - 它比
setTimeout(0)更早触发,且与屏幕刷新率同步,适合性能敏感场景 - 注意:若页面不可见(如切换标签页),
raf可能被节流或暂停,需配合visibilitychange判断
用 PerformanceObserver 监听长任务与空闲时段
现代浏览器提供 PerformanceObserver API,可主动订阅 longtask 和 event 类型,间接反映主线程负载变化。虽然不直接暴露“间隙”,但能识别出连续无任务的时段。
- 启用后可捕获超过 50ms 的长任务,其前后往往对应较明显的空闲窗口
- 搭配
performance.now()记录时间戳,可估算两次长任务之间的空闲长度 - 需注意:该 API 需显式启用,且部分旧环境不支持,建议作为补充手段而非主方案
避免误判:同步代码不等于间隙
写在两个 setTimeout 中间的同步代码,执行时机由调用栈决定,不是“间隙”。真正的间隙只存在于宏任务切换之间,与代码书写位置无关。
- 例如:
setTimeout(a, 0); console.log('mid'); setTimeout(b, 0)中的console.log是同步执行,属于第一个宏任务的一部分,不是间隙 - 要捕获间隙,必须把逻辑投递为新的宏任务,而非放在同步流程里
- 微任务(如
Promise.then)虽快,但它属于当前宏任务生命周期内,不能代表“任务间”间隙

















