首页 > 解决方案 > 为什么前 4 次执行没有限制 setInterval 调用?

问题描述

众所周知,JavaScript 中的超时在活动选项卡上每 4 毫秒被限制,在非活动选项卡上每 1000 毫秒被限制。此行为可能会略有变化,具体取决于浏览器和配置,但通常是相同的。

为了完整起见,我将在此处引用相关部分:

超时限制为 >=4ms

在现代浏览器中,当由于回调嵌套(其中嵌套级别至少为一定深度)或在一定数量的连续间隔后触发连续调用时, setTimeout()/调用至少每 4 毫秒一次。setInterval()

我想通过在默认 Chrome 实例上使用此代码段来向某人证明这一点:

let beforeStartingTheInterval = Date.now();
let intervalId = setInterval(()=>{ 
  let msPassed = Date.now()-beforeStartingTheInterval;
  if (msPassed > 20) clearInterval(intervalId); // stop after 20 ms
  console.log( msPassed + "ms have passed after starting the interval")
  }
,0);

几乎每次我运行该代码时,我都会得到以下输出:

1ms have passed after starting the interval
2ms have passed after starting the interval
3ms have passed after starting the interval
4ms have passed after starting the interval
8ms have passed after starting the interval
12ms have passed after starting the interval
16ms have passed after starting the interval
20ms have passed after starting the interval

可以看出,间隔(通常)前四次每毫秒运行一次,之后每隔 >=4ms。

为什么对于前 4 次执行,调用没有受到限制?我期望这个输出:

4ms have passed after starting the interval
8ms have passed after starting the interval
12ms have passed after starting the interval
16ms have passed after starting the interval
20ms have passed after starting the interval

或者至少一个在任意两次执行之间至少有 4ms 的执行(甚至是第一次执行)

标签: javascriptbrowsersetintervalthrottling

解决方案


为什么对于前 4 次执行,调用没有受到限制?

我想应该问的第一个问题是为什么计时器会受到限制?

好吧,有些时候可能是这样的短运行计时器

1)难以实现,因为您必须使用硬件计时器或检查循环内的时间,以便能够准确地安排时间,循环必须运行得非常快。

2)消耗大量电池/计算时间

3) 可能根本不需要,因为显示器大约每 60fps 更新一次,即每 16ms,所以如果你使用运行速度如此之快的计时器,你实际上不会看到那个速度的输出。

那么下一个问题可能是为什么要使用这么短的计时器呢?

其实有几个很好的理由:

1) 要在 a) 当前代码块之后推迟一个动作,以 React 的 setState 为例 b) DOM 重新渲染

2)在主线程上“异步”执行长时间运行的任务而不阻塞UI,通过释放它一毫秒时间。

因此浏览器在调整定时器时间时出现问题:

如果它将时间限制为例如setState4 毫秒,则页面可能整体加载速度较慢。但是,如果它不限制运行速度过快的 setInterval,则会浪费用户的电池(如今大多数用户都在使用移动设备)。

因此,浏览器非常快地执行几个计时器是有道理的,因此setTimeout(deferred, 0)会立即执行,但在几次之后就会受到限制,以减少错误制作的渲染计时器/长时间运行的算法的负面影响。

“5次”之后的“4ms”可能是这种考虑的平衡结果。


推荐阅读