js的eventloop中的宏任务和微任务理解,实现对setTimout定时器补偿
写有关宏任务和微任务的优秀博文有很多已经差不多讲的很详细,写这边博客并不是要做一个补充, 大略只是对自己学习理解做一个记录补充,同时进行分享。有助于输出推动学习。
外文链接:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly
event loop(事件循环)
我们js运行是单线程的,所以js的执行事件就需要按照顺序排列一项一项执行任务,当执行完成这次的之后就会等待下一次事件队列执行,如此循环往复就叫 eventLoop
同步&异步任务执行顺序
我们都知道js的代码运行分为同步和异步,同步代码不会等待异步代码执行完毕,而是先执行同步代码后执行异步代码。直接看图:(网上素材)
解释一下:
1.当一个一个事件循环开始时,同步事件直接放到主线程执行,异步事件放到event table并回调事件, 异步事件执行玩之后需要调用回调的时候,就到event queue中等待真正执行。
2.事件处理机制读取主线程任务并执行完毕。
3.之后从event queue中读取回调到主线程的事件执行
也就是说,当异步事件执行完之后回调并不是立即执行的,而是要等待主线程同步任务执行完毕
所以当我们使用setTimeout作为定时器时,经常会不精确,因为有可能主线程需要运算很久的话,定时器的回调就会一直等待同步代码执行完毕
function func() {
setTimeout(()=> {
console.log("异步")
},100)
/*
一段需要大量计算的同步代码... (耗时3s)
*/
console.log("同步")
}
//控制台
同步
异步
这对于抽奖这种需要时间很准确的场景来说,就显得很严重了,所以我们尝试通过定时器补偿的方法来提高精确度
let start = new Date().getTime(),
time = 0,
before = '0.0';
function instance()
{
time += 100;
before = Math.floor(time / 100) / 10;
if(Math.round(before) == before) { before += '.0'; }
document.title = before;
var diff = (new Date().getTime() - start) - time;
setTimeout(instance, (100 - diff));
}
setTimeout(instance, 100);
宏任务&微任务
宏任务和微任务都是异步执行的事件,但是又分执行先后。
宏任务主要包括: 执行script代码块,setTimeout,setInterval、setImmediate。
微任务包括: 原生Promise、process.nextTick、Object.observe、 MutationObserver
其中我们最常见的就是 setTimeout,setInterval,Promise这几个
//代码
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
//输出
script start
script end
promise1
promise2
setTimeout
从上面我们可以看出执行顺序是:
同步任务 => Promise回调(微任务) => setTimeout(宏任务)
但是其实真正的执行顺序是宏任务先被放到event Table中注册执行,微任务后被放到event Table中注册执行,但是最后主线程是从event Queue先取出微服务的回调执行,宏服务的回调后执行。
如图:(网上素材)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/888c20ce4e1562a01382544f3f9c36bf.png#pic_center)
总来的来说了解event loop事件循环处理机制还是很有必要的,让我们了解为什么代码是这样执行的,可以避免不必要的错误。