浏览器为啥要对 JavaScript 定时器“踩刹车”?


                                                                                                                                                <p>各位开发者朋友,不知道你有没有遇到过这种情况:你写了个 setTimeout(fn, 0),满心期待着它立刻执行,结果它却“磨蹭”了大概 4 毫秒才跑起来?别怀疑,这不是你的代码写错了,而是浏览器在背后悄悄“搞了小动作”!今天咱们就来聊聊,浏览器为啥要对这些定时器“节流”(throttle),以及面对这些限制,我们又能有哪些新选择。</p> 

⏰ 1. 定时器并不“准时”

如果你尝试过下面这段代码:

const start = performance.now();
setTimeout(() =&gt; {
  console.log(performance.now() - start); // 输出大概是4毫秒
}, 0);

你会发现,即便是设置为 0 毫秒延迟的 setTimeout,实际执行也会花费大约 4 毫秒。这是因为浏览器为了避免开发者“滥用”定时器(比如疯狂循环调用 setTimeout),从而设置了最低 4 毫秒的延迟限制。这样一来,可以防止某些网站过度消耗用户的电池电量或者阻塞页面交互。

浏览器的“节流策略”还会因情况而变化

  • 设备使用电池时:比如旧版的 Edge 浏览器会将延迟提升到 16 毫秒
  • 标签页处于后台时:Chrome 对后台标签页的定时器延迟甚至可能达到 1 秒!😱

🤔 2. 既然节流,为何还出新 API?

看到这里,你可能会有个疑问:既然 setTimeout 因为被滥用而被限制了,那为什么浏览器们还在不断推出新的定时器 API 呢?比如已经“凉了”的 setImmediate,或是 Promise,又或是新秀 scheduler.postTask()?难道它们最终不会面临同样的“被节流”的命运吗?

这个问题也困扰了原文作者很久。直到他在开发一个纯 JavaScript 实现的 IndexedDB API(fake-indexeddb)时,才重新审视了这个问题。IndexedDB 希望在事件循环中没有未完成的任务时自动提交事务,换句话说,就是在所有微任务(microtasks)完成后,但任何宏任务(比方说 setTimeout 这样的“任务”)开始之前。

为了模拟这个过程,fake-indexeddb 在 Node.js 中使用了 setImmediate(这很合适,因为它恰好在微任务之后、其他任务之前执行,且没有延迟限制),在浏览器中则不得不使用 setTimeout。结果呢?作者发现在 Chrome 中某个操作需要 4.8 秒,而在 Node 中仅需 300 毫秒(慢了 16 倍!) .

🧪 3. 2025 年,我们有哪些定时器选择?

既然 setTimeout 在浏览器中表现如此不尽人意,那在 2025 年的今天,我们有没有更好的选择呢?作者测试了一些方案:

浏览器 setTimeout MessageChannel window.postMessage scheduler.postTask
Chrome 139 4.2 ms 0.05 ms 0.03 ms 0.00 ms
Firefox 142 4.72 ms 0.02 ms 0.01 ms 0.01 ms
Safari 18.4 26.73 ms 0.52 ms 0.05 ms 未实现

测试数据为 101 次迭代的中位数(单位:毫秒)

从表格中可以清晰地看到:

  • setTimeout:在各个浏览器中都有明显的延迟(Safari 尤其突出)。
  • MessageChannel.postMessagewindow.postMessage:表现比 setTimeout 好很多,延迟显著降低。
  • scheduler.postTask在 Chrome 和 Firefox 中表现最佳,延迟极低甚至为 0,但目前 Safari 尚未实现。

所以,如果你现在需要高精度的定时器,scheduler.postTask 似乎是当下的最佳选择(当然,要考虑 Safari 的兼容性问题)。

💎 4. 总结与启示

  • 浏览器对 setTimeout 和 setInterval 等定时器的节流,主要是出于性能和保护用户电池寿命的考虑,防止糟糕的代码拖垮整个浏览器或设备。
  • 这种节流策略并非一成不变,它会根据设备是否接通电源、标签页是否处于后台等因素动态调整。
  • 对于需要高精度定时对性能敏感的场景(比如实现动画、模拟 IndexedDB 事务等),传统的 setTimeout 可能不再是最佳选择。
  • 可以关注并尝试使用新的 scheduler.postTask API(当然,要准备好回退方案),或者根据实际情况考虑 MessageChannel 等替代方案。
  • 并非所有定时器任务都需要高精度。对于很多日常任务(如简单的延迟、轮询),setTimeout 和 setInterval 仍然完全够用且兼容性最好。

希望这篇译文能帮你理解浏览器定时器背后的“小秘密”,以及如何在不同的场景下做出更合适的选择。如果你在项目中遇到过定时器的“坑”,或者对新的定时器 API 有使用经验,欢迎在评论区分享交流! 🎉

原文链接:https://nolanlawson.com/2025/08/31/why-do-browsers-throttle-javascript-timers/

原文作者:Nolan Lawson原文作者:Nolan Lawson

                                                                                </div>



Source link

未经允许不得转载:紫竹林-程序员中文网 » 浏览器为啥要对 JavaScript 定时器“踩刹车”?

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的