第6层起settimeout(0)强制≥4ms是html标准规定的节流机制;前5次约0.3–1.2ms,第6次起跳至4–6ms;层级按宏任务调用链累计,与缩进无关;非活跃标签页延迟升至1000ms或10000ms。

深度嵌套的 setTimeout 会触发浏览器的主动节流机制,不是代码写错了,而是 HTML 标准强制规定的保护策略。
第6层起最小延迟被拉高到 ≥4ms
从第6次连续宏任务调度开始(即调用链上第6个 setTimeout),哪怕你传的是 0,浏览器也会把实际延迟强制设为至少 4 毫秒。前5次通常还能保持在 0.3–1.2ms 左右,但第6次起会突然跳变。这个“层”按宏任务调用路径累计,和函数缩进、是否加了 Promise.then 都无关——只要它是通过 setTimeout 触发的下一层,就算一层。
非活跃标签页会进一步恶化延迟
用户切走当前页面后,浏览器会大幅降低定时器精度来省电:
- Chrome / Firefox 桌面版:
setTimeout(0)实际延迟 ≥1000ms(1秒) - Firefox 开启脚本追踪后:后台标签中延迟直接升至 ≥10000ms(10秒)
- 这个限制独立于嵌套层级,但常被误认为是“嵌套更深导致的”,其实只是叠加效应
不同浏览器实现略有差异,但都遵循节流逻辑
虽然标准写的是“嵌套层级 ≥6”,但各引擎落地时有细微差别:
- Chrome 和 Firefox:第6层起执行 ≥4ms
- Safari:行为接近,但部分版本可能略晚触发
- Edge:旧版曾设为第3层就限速,新版已对齐主流
- Node.js 不受此限:它没有页面生命周期管理,
setTimeout(0)可低至 0.1ms
绕不过去,但可以换更合适的工具
别费劲“欺骗”浏览器计数(比如用 postMessage 或 requestIdleCallback 模拟零延迟),现代浏览器已把这些旁路也纳入统一节流范围:
- 需要高频、精准调度?优先用
requestAnimationFrame(动画场景)或 Web Audio API(音频同步) - 只想尽快脱出当前调用栈?
queueMicrotask是更轻量的选择,不计入宏任务层级,也不受 4ms 限制 - 做防阻塞或状态更新?避免嵌套
setTimeout,改用 Promise 链或async/await更可控











