之前我们谈到过JavaScript的异步机制与event loop【JavaScript】事件执行机制、同步与异步、事件循环(event loop),谈到JavaScript分为同步任务和异步任务,但其实JavaScript又把异步任务做了进一步的划分,细分成了宏任务与微任务。
一、宏任务与微任务介绍
宏任务(macrotask) 一般包括:
- 异步Ajax请求
- 定时器(setTimeout、setInterval)
- 文件操作
- 其他宏任务
【注意】 整体script也被看做是一个宏任务。
微任务(microtask) 一般包括:
- Promise.then、.catch 和 .finally
- process.nextTick
- 其它微任务
二、宏任务与微任务的执行机制
每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务。也就是说,每执行完一个宏任务,都要查看并清空微任务队列,才继续执行下一个宏任务。
我们分析一下任务执行过程:
- 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务(即整个script脚本),放入调用栈,开始执行;
- 按照代码顺序,依次执行该宏任务下的所有同步任务,期间如果遇到其他宏任务或者微任务,将其推入对应任务队列中。
- 执行完该宏任务下所有同步任务后,即调用栈清空后,该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序,依次执行其中的微任务,直至微任务队列清空为止;
- 当微任务队列清空后,一个事件循环结束;
- 接着从宏任务队列中,找到下一个执行的宏任务,重复1-4,开始第二个事件循环,直至宏任务队列清空为止。
【注意】
- 1 因为整体script也被看做是一个宏任务,所以一开始整个script脚本都被推入宏任务中,事件循环是从第一个宏任务开始的。
- 2 如果在执行微任务的过程中,产生新的微任务添加到微任务队列中,也需要一起清空;微任务队列没清空之前,是不会执行下一个宏任务的。
回到刚才阅读的位置
三、举例说明
看一段代码,分析一下他的输出结果:
console.log("a");
setTimeout(function () {
console.log("b");
new Promise((resolve) => {
console.log("c");
resolve();
})
.then(function () {
console.log("d");
})
}, 0);
new Promise((resolve) => {
console.log("e");
resolve();
})
.then(function () {
console.log("f");
})
.then(function () {
console.log("g");
});
console.log("h");
- 首先,整个代码都被推入宏任务队列中,开始从第一行执行同步任务,第一行在控制台打印
'a'
;(这里体现了注意第一点) - 然后看到setTimeout函数,整体被推入宏任务队列中;
- 代码直接来到new Promise这一行,new Promise函数是立即执行的,所以在控制台打印
'e'
,执行resolve(),然后第一个.then被推入微任务队列; - 然后继续执行下一个同步任务,代码来到最后一行,在控制台打印
'h'
。
到此时第一个宏任务被执行完毕,然后它就被移出宏任务队列,开始清空微任务队列。
- 此时微任务队列中,有第一个.then在排队,所以进入第一个.then,在控制台打印
'f'
; - 执行完第一个.then的同时,发现后面还有一个.then,这时第二个.then被推入微任务队列,第一个.then执行完毕,被移出微任务队列;
- 此时微任务队列还是没有被清空,有第二个.then在排队,所以与上面类似,在控制台打印
'g'
。 - 第二个.then执行完毕,被移出微任务队列。(这里体现了注意第二点)
此时微任务队列被清空。完成了第一个event loop。然后进入第二个宏任务,也就是setTimeout函数。
- 与上面的步骤基本类似,同样先从同步任务开始,在控制台打印
'b'
; - 然后来到new Promise这一行,在控制台打印
'c'
,执行resolve(),然后 .then被推入微任务队列; - 当前宏任务完成,被移出宏任务队列,开始清空微任务队列,进入.then函数,在控制台打印
'd'
; - 当前微任务完成,被移出微任务队列;
- 微任务队列已被清空,没有新的微任务了。
此时所有的代码都运行结束了。运行输出结果为:aehfgbcd
。
四、总结
本文详细介绍了宏任务与微任务及其运行机制,并通过一个例子更加具体的演示了宏任务与微任务如何执行,关于async和await、页面渲染中涉及到的宏任务与微任务运行机制,可以参考做一些动图,学习一下EventLoop(来源于稀土掘金Dewey)。