JavaScript事件循环:宏任务与微任务
- 宏任务:主要包括定时器、事件交互(DOM)、
AJAX请求、I/O、UI等
- 微任务:主要包括
Promise回调、MutationObserver回调、process.nextTick(使用于node.js中)
在执行JavaScript的过程中,宏任务与微任务的执行时机很重要。因为JS为单线程语言,所以在执行时会将以上两种任务按优先级添加到任务队列中,再由主线程不断读取任务队列,这个过程不断循环,因此称为事件循环。
任务队列的执行顺序:先到队列中读取可执行的微任务,然后执行读取到的微任务;再到队列中读取可执行的宏任务,然后执行读取到的宏任务。每次准备取出第一个宏任务执行之前,都要将所有的微任务一个一个取出来执行。 来看一下例子。
console.log ( 'script start' ) ;
setTimeout ( () => {
console.log ( 'timeout1' ) ;
} ,100 )
setTimeout ( () => {
console.log ( 'timeout2' ) ;
new Promise ( resolve => {
console.log ( 'promise1' ) ;
resolve ()
}) .then ( () => {
console.log ( 'then1' ) ;
})
} ,100 )
Promise.resolve () .then ( () => {
console.log ( 'then2' )
})
new Promise ( resolve => {
console.log ( 'promise3' ) ;
resolve () ;
setTimeout ( () => {
console.log ( 'timeout in promise3' )
} ,100 )
}) .then ( () => {
console.log ( 'then3' ) ;
setTimeout ( () => {
console.log ( 'timeout in then3' )
} ,100 )
})
console.log ( 'script end' ) ;
解析:
-
JS从上到下执行,属于宏任务:
- 输出
script start - 将
timeout1添加到宏任务队列,待执行 - 将
timeout2添加到宏任务队列,待执行 - 将promise回调
then2添加到微任务队列,待执行 - 执行promise,输出
promise3,将回调then3添加到微任务队列中,将timeout in promise3添加到宏任务队列中 - 输出
script end
- 输出
宏任务:[ timeout1,timeout2,timeout in promise3 ];微任务:[ then2,then3 ]
-
依次读取并执行微任务队列:
- 输出
then2 - 输出
then3,将timeout in then3添加到宏任务队列中,待执行
- 输出
宏任务:[ timeout1,timeout2,timeout in promise3,timeout in then3 ];微任务:无
-
依次读取并执行宏任务队列:
- 输出
timeout1 - 输出
timeout2,执行promise,输出promise1,将回调then1添加到微任务队列中
- 输出
宏任务:[ timeout in promise3,timeout in then3 ];微任务:[ then1 ]
-
依次读取并执行微任务队列:
- 输出
then1
- 输出
宏任务:[ timeout in promise3,timeout in then3 ];微任务:无
-
依次读取并执行宏任务队列:
- 输出
timeout in promise3 - 输出
timeout in then3
- 输出
需要注意: 这里的setTimeout中timeout参数都一致,如果是不同的参数,则按定时器的原理,定时短的先输出,但并不意味其优先级高。
简单总结一下执行的顺序: 执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。