You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将零延迟setTimeout放在函数开头代码执行更快的原因是什么?

两种场景的核心差异先明确

我们先把两种写法的核心逻辑抽离出来,方便对比:

写法1(慢版本:计算完成后调度下一次)

function count() {
  // 先执行100万次运算
  do { i++ } while (i % 1e6 != 0)

  if (i < 1e7) {
    // 运算完成后才发起下一次定时器调度
    setTimeout(count, 0)
  }
}

写法2(快版本:进入函数先调度下一次)

function count() {
  // 进入函数第一时间就发起下一次定时器调度
  if (i < 1e7) {
    setTimeout(count, 0)
  }
  // 再执行100万次运算
  do { i++ } while (i % 1e6 != 0)
}

耗时差异的核心原因:浏览器的setTimeout嵌套延迟规则

HTML标准明确规定:当setTimeout的嵌套调用层数≥5时,若设置的延迟小于4ms,浏览器会强制将延迟调整为4ms。这个规则的初衷是避免过密的定时器调度挤占主线程资源,导致页面交互卡顿。

两种写法的调度链路差异

慢版本的调度链路:触发嵌套延迟,等待时间串行累加

每一次setTimeout调用,都是在上一次定时器触发的count函数执行末尾才发起的,形成了严格的嵌套调用链:

  1. 初始调用count()→计算完成→发起第1次setTimeout(count,0)
  2. 第1次定时器触发执行count→计算完成→发起第2次setTimeout(count,0)
  3. 以此类推...
    当调用到第6次setTimeout时,嵌套层数已经达到阈值,后续每一次调度都会被强制加上4ms的额外延迟。整个流程需要调度9次(10批计算,最后一批不需要调度),后面5次调度总共会产生至少20ms的额外延迟,且延迟和计算时间是串行累加的,整体耗时就会变长。

快版本的调度链路:等待时间与计算时间重叠,无额外损耗

进入count函数的第一时间就发起下一次setTimeout调度,此时当前批次的计算还没开始:

  1. 就算触发了4ms的嵌套延迟,这个等待时间也会和当前批次的计算时间(单批100万次运算通常耗时5-10ms)重叠,等当前计算完成时,下一次定时器的等待时间已经跑完,回调可以直接执行
  2. 不会出现慢版本中“计算完成→等4ms→再计算下一批”的串行等待场景,所有额外等待时间都被计算过程覆盖,整体执行速度自然快很多。

内容的提问来源于stack exchange,提问作者Nikita Vlasenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 02:06:02