对JavaScrip中event loop 任务与微任务的一点认识

作者 likaiqiang 日期 2021-06-10
对JavaScrip中event loop 任务与微任务的一点认识

今天在知乎上看了一篇关于宏任务/微任务的文章 ,竟然看懂了6、7成。大概意思是event loop 分为宏任务/微任务,像我们平时的ui事件,setTimeout、setInterval的回调都会放入宏任务中,而promise、mutationobserver则会进入微任务。在浏览器的一次render中,同步任务执行完毕后,首先会检查宏任务中有没有优先级很高的操作(比如ui事件),如果有会优先执行,没有则执行微任务,如果在清空微任务队列中又有新的微任务被添加进来,继续执行微任务,直至清空微任务队列,然后再去执行宏任务(如此循坏)。

文中还附了一个vue nextTick的例子,算是小小的福利

简而言之,dom的更新是同步的,那怎样才能在dom更新后获取通知呢,答案是利用eventloop,同步的dom更新结束后总会去执行eventloop的,vue作者在某个vue版本中使用
window.postMessage来实现nextTick,
window.postMessage是宏任务(优先级不高的宏任务),而ui交互事件scroll是优先级高的宏任务,所以可能出现浏览器render了好几次都轮不上执行postMessage。为了解决这个问题,vue作者依然采用Promise与MutationObserver来实现nextTick。

理解了宏任务与微任务之间的关系,那些曾经认为该死的笔试题终于知道其目的了,还是挺有用的~

案例一

setTimeout(()=>{
console.log(1) //放入宏任务
})
requestAnimationFrame(()=>{
console.log(2) // 既不属于宏任务,也不属于微任务,会在render前执行
})
setTimeout(()=>{
console.log(4) //放入宏任务
})
Promise.resolve(3).then(res=>{
console.log(res) // 放入微任务
})

3 2 1 4

分析:第一轮loop,扫描有没有优先级高的宏任务,没有,执行微任务输出3,清空微任务队列,执行requestAnimationFrame,输出2,执行剩下的宏任务。下一轮loop。

案例二

let thenable = {
then: function (resolve, reject) {
resolve(42);
}
};
new Promise(resolve => { // 标记3
resolve(1)
Promise.resolve(thenable).then((t) => { // 标记4
console.log(t)
});
console.log(4) // 标记1
}).then(t => {
console.log(t)
});
console.log(3); //标记2

4 3 1 42

分析:标记1、2是同步代码,先执行,标记3、4会依次被添加到微任务队列中。所以结果是4 3 1 42

案例三(还是知乎上那篇文章,有人在评论区给了个例子)。

var timeout = 0
console.log(1)
setTimeout(() => {
console.log(2)
}, timeout)
new Promise((resolve) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
requestAnimationFrame(_ => console.log(5))
console.log(6)

输出1 3 6 4 5 2。但是5有时候会出不来,不知道怎么回事?