Event Loop、 宏任务和微任务
本文将介绍我自己对JS Event Loop
和 宏任务、微任务
的理解。
二话不说先上图:
接下来将会针对此图讲解什么是Event Loop 什么事宏任务和微任务(其实聪明的你们通过图大体也能了解的是吧~),再此之前先简单介绍几个概念。
为何js是单线程
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出 Web Worker 标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。(此段来自阮老师)
概念
- 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
- 异步任务:不进入主线程、而进入
任务队列
(Task Queue)的任务。 - 任务队列:是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
详解图示
- 当一段代码在主线程执行时,会有同步任务和异步任务,异步任务会判断是微任务还是宏任务,进行不同处理。然后继续执行当前主线程内的代码,直到结束。此段主线程执行的的为宏任务。
- 接着上面的判断,宏任务的异步操作会先进入
Event Table
执行,然后当执行结束会把回调函数推入到Event Queue
,然后等待主线程的结束。对于微任务的异步会推入到另外一个Event Queue
,等待主线程的结束。 - 当主线程结束后,先执行所有的微任务,然后执行把宏任务的任务队列(
Event Queue
)中的event拉到主线程执行。 - 一直循环前面的操作。至此你应该懂的
EventLoop
了。
备注:如果在执行 microtask(微任务) 的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行。
举个例子🌰
setTimeout(function() {
console.log('setTimeout');
}, 0)
new Promise(function(resolve) {
console.log('promise 1');
resolve();
console.log('promise 2')
}).then(function() {
console.log('promise then');
})
console.log('1');
输出结果:promise 1 、promise 2 、1 、promise then 、setTimeout
解释:
- 这段代码作为宏任务,进入主线程。
- 先遇到setTimeout,则进入Event Table执行并注册,然后将其回调函数发到宏任务Event Queue。
- 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。里面的console.log(),立即执行。
- 继续,遇到console.log(),立即执行。
- 至此这个主线程的宏任务执行结束,接着查找微任务,发现then的回调然后执行。
- 第一轮事件循环结束了,我们开始第二轮循环,从宏任务Event Queue开始执行。即宏任务Event Queue中setTimeout对应的回调函数,立即执行。
- END。
为何会有宏任务和微任务的说法。
eventloop中处理微任务和宏任务的逻辑也是不同厂商按照规范实现的 jsvm。
补充几个宏任务和微任务
宏任务 | 浏览器 | Node |
---|---|---|
setTimeout | √ | √ |
setInterval | √ | √ |
setImmediate | x | √ |
requestAnimationFrame | √ | x |
微任务 | 浏览器 | Node |
---|---|---|
process.nextTick | x | √ |
MutationObserver | √ | x |
Promise.then catch finally | √ | √ |
来自我的简书的文章:Event Loop、 宏任务和微任务