Skip to content
字数
980 字
阅读时间
4 分钟

执行时机

setTimeout 和 setInterval 的运行机制是将指定的代码移出本次执行,等到下一轮 Event Loop 时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮 Event Loop 时重新判断

js
f1()

setTimeout(() => {
    fn()
}, 2000)

f2()

当定时时间到了后,才会将 setTimeout 的回调函数放入消息队列中,但是具体执行的时机并不只是由定时时间决定的,还取决于当前的执行栈、当前 event loop 的事件执行时间,所以定时时间相当于一个起步时间

js
// 效果: 同时打印3个2
for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(new Date());
    }, 1000);
}

// 等价于

(function () {
	setTimeout(function () {
        console.log(new Date());
	}, 1000);
	
	setTimeout(function () {
	        console.log(new Date());
	}, 1000);
	
	setTimeout(function () {
	        console.log(new Date());
	}, 1000);
})()

在达到 2000ms 时的那轮 event loop,所有 timer 都达到规定时间可以执行,也就产生了同时响应的效果

setTimeout(fn, 0) 作用

指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。

它在 " 任务队列 " 的尾部添加一个事件,因此要等到主线程把同步任务和 " 任务队列 " 现有的事件都处理完,才会得到执行

为什么最小延迟是 4ms

示例程序:

js
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)

html 标准

详情参阅:HTML Standard

第 4 条:如果设置的 timeout 小于 0,则设置为 0(理想的 最低时延
同时,这个 ms 数值如果是浮点数也会被看作 舍去小数的整数部分

为什么不能是 0

其原因在于如果浏览器允许 0ms,会导致 JavaScript 引擎过度循环,也就是说如果浏览器架构是单进程的,那么可能网站很容易无响应。

因为浏览器本身也是建立在 event loop 之上的,如果速度很慢的 JavaScript engine 通过 0ms timer 不断安排唤醒系统,那么 event loop 就会被阻塞。那么此时用户会面对什么情况呢?同时遇到 CPU spinning 和基本挂起的浏览器,想想就让人崩溃。

如果一个浏览器经常让用户体验到这种情况,绝对没人愿意用的,毕竟很少有人愿意受虐。这也是为什么 chrome 1.0 beta 设置的是 1ms。

CPU 固定和 CPU 集

目前逻辑

chromium 源码:

  1. chrome 最小阈值为 1ms,写 0 和 1 本质都是 1 (1ms 存疑,论证以下面的程序运行结果示例
  2. 如果嵌套的层级超过了 5 层,并且 timeout 小于 4ms,则设置 timeout 为 4ms

运行结果:

如果最低时延是 1ms,那么 "1" 应该在 "0" 之前显示
TODO:原因待求证

在 chromium 中的实现机制

Chromium setTimeout 的实现原理是:

  1. 延迟任务队列存储所有延迟任务
  2. 消息循环检查并执行已经到期的延迟任务
  3. 执行完已到期延时任务后,找到延迟任务队列中延迟时间最短的任务,记该任务的延迟时间为 x
  4. 调用操作系统 定时器函数,定时 x
  5. x 时间后,回到步骤 2

Chromium 源码版本 91.0.4437.3,以 setTimeout(_ => {}, 100) 为例

详情参考:JS 中 setTimeout 的实现机理是什么? - 知乎

参考

【 js 基础 】 setTimeout(fn, 0) 的作用

setTimeout 放入消息队列的时机

为什么 setTimeout 有最小时延 4ms ?

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写