聊聊Js计时器精准度

闲言

在上篇文章中,我们系统了解了 Js 运行机制,我们接着来说说 Js 计时器的精准度问题。
计时器功能主要由 setTimeout() 和 setInterval() 这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。
我们直接来看一些例子吧。

定时器时延无法保证

setTimeout:

let time1 = new Date().getTime();
setTimeout(() => {
 let time2 = new Date().getTime();
 console.log("setTimeout执行的差值时间:" + (time2-time1)/1000 + 's');
},1000);

for (let i = 0; i < 10000; i++) {
    console.log('setTimeout');
}
/*
10000setTimeout
setTimeout执行的差值时间:1.206s
*/

大家可以去浏览器跑一下这段代码,会发现每次结果都不一样,相差的秒数都是大于 1s 的,因为 setTimeout 的回调执行要等到主线程上的 for 循环跑完。
说明:HTML5 标准规定了 setTimeout() 的第二个参数的最小值(最短间隔),不得低于 4 毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。
setInterval:

let time1 = new Date().getTime();
setInterval(() => {
 let time2 = new Date().getTime();
 console.log("setInterval执行的差值时间:" + (time2-time1)/1000 + 's');
}, 1000);

/*
setInterval执行的差值时间:1.001s
setInterval执行的差值时间:2.001s
setInterval执行的差值时间:3.003s
setInterval执行的差值时间:4.004s
setInterval执行的差值时间:5s
setInterval执行的差值时间:6.006s
setInterval执行的差值时间:7.004s
setInterval执行的差值时间:8.003s
setInterval执行的差值时间:9.005s
setInterval执行的差值时间:10.004s
*/

对于 setInterval,都不需要让它去等待主线程,它本身就不准。

最后

我在这篇博客 https://fanyi.baidu.com/translate#en/zh/ (需要梯子)中看到这张图:

我们注意到图中标红的 Dropped! ,作者观点的大致意思是:setInterval 的回调在任务队列排队阶段,若此时未等到执行,并又一次触发回调时(达到指定的时延),该回调将会被移除。
即队列中保证只能有一个 setInterval 触发的回调。