setTimeout最小间隔4ms的问题

setTimeout(()=>{console.log(5)},5)
setTimeout(()=>{console.log(4)},4)
setTimeout(()=>{console.log(3)},3)
setTimeout(()=>{console.log(2)},2)
setTimeout(()=>{console.log(1)},1)
setTimeout(()=>{console.log(0)},0)

为什么输出1,0,2,3,4,5,不是说setTimeout最小间隔4ms吗?
运行环境Chrome/Safari
在Firefox下为0,1,2,3,4,5

阅读 15.5k
9 个回答
// https://github.com/nodejs/node/blob/v8.9.4/lib/timers.js#L456

if (!(after >= 1 && after <= TIMEOUT_MAX))
  after = 1; // schedule on next tick, follows browser behavior

设置最低1ms的行为是为了向浏览器行为看齐。

虽然有4ms的限制,但是是存在条件的.详见MDN英文文档

clipboard.png

最小间隔4ms的说法是不准确的,或者说是有前提条件的,请看HTML标准

11. If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4.

也就是说,循环嵌套超过5层的,并且延迟不到4ms,才会变成4ms
标准是这么规定的,各个浏览器具体怎么定义的,我没有亲测~

setTimeout和setInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout指定的代码,必须等到本次执行的所有代码都执行完,才会执行。

执行完setTimeout(()=>{console.log(1)},1)的时候一个毫秒已经过去了, 而且console.log(1)已经先进入了事件循环中, 于是就console.log()`

参考:setTimeout运行机制

setTimeout的第二个参数,不得低于4毫秒,如果低于这个值,就会自动增加,根据浏览器和当前环境不同,最低时间间隔也不一样,浏览器执行的顺序应该和浏览器当前状态有关系,我在firebox执行的结果是0 1 5 4 3 2

复现楼主问题:

  setTimeout(()=>{console.log(5)},5)
  setTimeout(()=>{console.log(4)},4)
  setTimeout(()=>{console.log(3)},3)
  setTimeout(()=>{console.log(2)},2)
  setTimeout(()=>{console.log(1)},1)
  setTimeout(()=>{console.log(0)},0)

打印的日志:
图片描述

下面是补充点我自己发现的信息

  setTimeout(()=>{console.log(5)},5)
  setTimeout(()=>{console.log(4)},4)
  setTimeout(()=>{console.log(3)},3)
  setTimeout(()=>{console.log(2)},2)
  setTimeout(()=>{console.log(0)},0)
  setTimeout(()=>{console.log(1)},1)

当是这种顺序的时候,chrome和safari俩个浏览器下会打印
图片描述

6个setTimeout在本次event loop依次将回调函数放入task队列,在未来等待event loop检测到了指定时间时执行,先压入的回调先执行;出现不同的执行顺序,可以理解为,当下次的event loop执行时,如果已经超过1ms,就先执行console.log(1)的回调(因为队列中它更靠前);而如果未超过1ms,则执行console.log(0)的回调。

见过一篇v8源码分析里面说的 chrome对timeout的实现最小是1ms 所以无论延时设置1和0 都仿佛同步代码按顺序执行 4ms指的是多层timeout嵌套情况下 最小4ms 文章我找不到了

chrome 下是因为设置 1ms 或者是 0ms 都是 1ms,所以输出是 1, 0 而不是 0, 1

源码地址
double intervalMilliseconds = std::max(oneMillisecond, interval * oneMillisecond);

参考文章地址:Event Loop的规范和实现

新手上路,请多包涵
推荐问题
宣传栏