js基础 ---- 为什么定时器时间不准确
一、为什么会出现定时器不准确
这个其实就得提到js执行机制了,叫做事件循环Eventloop 循环机制中,异步事件 setInterval 到时后会把回调函数放入消息队列中Event Queue,主线程的宏任务执行完毕后依次执行消息队列的微任务,等微任务执行完了在循环回来执行宏任务。并且由于消息队列中存在大量任务,其他任务执行时间就会造成定时器回调函数的延迟,如果不处理则会一直叠加延迟
二、宏任务和微任务
js可分为同步任务和异步任务,对于同步的任务,我们当然知道按照顺序进行执行,但是对于异步的操作,会有一个优先级的执行顺序,分别为宏任务和微任务
宏任务:setTimeout, setInterval, setImmediate, I/O, UI rendering
宏任务是一个外部脚本文件,一个用户交互触发的事件或一个setTimeout调用的回调函数。为了实现单线程这个概念,js有一个宏任务队列(先进先出),宏任务不断地创建出来塞到队尾,js引擎不断地从队首取任务出来执行。
微任务:process.nextTick, Promises, MutationObserver
微任务是由Promise创建出来的且js中有一个专门的微任务队列来存储微任务。微任务的机制是:当执行完一个任务后,只要有微任务就先执行微任务。宏任务和渲染通通排到后面。
三、解决方法
根据定时器最开始时间计算当前时间(回调函数执行时间)与开始时间的误差,用期望时差减误差作为下一次任务的时间间隔
var startTime = new Date().getTime();
var count = 0;
//耗时任务
setInterval(function(){
var i = 0;
while(i++ < 100000000);
}, 0);
function handle() {
count++;
var offset = new Date().getTime() - (startTime + count * 1000);
var nextTime = 1000 - offset;
if (nextTime < 0) nextTime = 0;
setTimeout(handle, nextTime);
console.log(count + ' --- ' + (new Date().getTime() - (startTime + count * 1000)));
}
setTimeout(handle, 1000);