在阅读《React进阶实践指南》的调度与时间片章节时,作者提到setTimeout(fn,0)在循环调用时,最后的时间间隔会变成4ms左右,但作者没有详细解释,所以我上网查了资料,并且在这里记下来。
第一问:这是行业制定的标准 还是 Bug
在第一次看到setTimeout(fn,0)循环调用最后时间间隔为4ms时,我还认为这是一个bug,后来发现是自己想当然了。因为这是在HTML Standard (whatwg.org)里明确规定过的
image.png
如图上所示,HTML Standard里明确指出了使用setTimeout时,
- 如果设置的timeout小于0,则设置为0
- 如果嵌套的层级超过了 5 层,并且 timeout 小于 4ms,则设置 timeout 为 4ms
但其实各大浏览器厂商并没有完全按照这个标准实现,比如我在Edge浏览器(Chromium内核)下执行多个setTimeout命令,结果如下所示
image.png
可以看到1比0先执行。
这是因为Chromium源码里规定了在不满足嵌套层级时,timeout最小值限制为1ms,这就意味着setTimeout(()=>console.log(0),0) 相当于 setTimeout(()=>console.log(0),1);
这也是这个例子中1比0先打印的原因
第二问:为什么要制定这4ms的延时
因为浏览器本身也是基于event loop的,所以如果浏览器允许0ms,可能会导致一个很慢的js引擎不断被唤醒,从而引起event loop阻塞,对于用户来说就是网站无响应,这是让人很难接受的。
所以chrome 1.0 beta限制为1ms。但是后来发现1ms也会导致CPU spinning,计算机无法进入睡眠模式,经过多次实验后,Chorme团队选定了4ms,之后主流浏览器纷纷采用了这个4ms的设定。