为什么前 4 次执行的 setInterval 调用没有受到限制?

Why are setInterval calls not throttled for the first 4 executions?

众所周知,JavaScript 中的超时受到限制 every 4ms on an active tab and every 1000ms on an inactive one。此行为可能会略有不同,具体取决于浏览器和配置,但通常是相同的。

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

Timeouts throttled to >=4ms

In modern browsers, setTimeout()/setInterval() calls are throttled to a minimum of once every 4ms when successive calls are triggered due to callback nesting (where the nesting level is at least a certain depth), or after certain number of successive intervals.

我想通过在默认 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

或者至少在任意两次执行之间(即使是第一次执行)至少间隔 4 毫秒

答案在documentation that you linked to(强调我的)

In Chrome and Firefox, the 5th successive callback call is clamped; Safari clamps on the 6th call; in Edge its the 3rd one. Gecko started to treat setInterval() like this in version 56 (it already did this with setTimeout(); see below).

在网上搜索了一些之后,我发现这是一个 HTML5 standard:

Note: Timers can be nested; after five such nested timers, however, the interval is forced to be at least four milliseconds.

Why is it that for the first 4 executions, the calls are not throttled?

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

好吧,有些原因可能是,这么短的 运行ning 定时器是

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

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

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

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

实际上有几个很好的理由:

1) 推迟行动 a) 当前代码块,以React的setState为例 b) DOM 重新渲染

2) 在不阻塞 UI 的情况下在主线程上执行长 运行ning 任务 "asynchronously",通过释放它一毫秒一段时间。

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

如果它限制了例如setState 4 毫秒,页面整体加载速度可能会变慢。但是,如果它不限制 运行 过快的 setInterval,它会浪费用户的电池(现在大多数用户都在使用手机)。

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

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

https://humanwhocodes.com/blog/2011/12/14/timer-resolution-in-browsers/

Most browsers also do some sort of timer throttling based on different conditions. The intent is to save battery at opportune times – times when, theoretically, you either won’t notice the difference or would gladly trade for improved battery life on a laptop or mobile device. Here are some circumstances where timer resolution changes: