JavaScript事件循环:宏任务与微任务

113 阅读2分钟

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' ) ;

解析

  1. JS从上到下执行,属于宏任务:

    1. 输出script start
    2. timeout1添加到宏任务队列,待执行
    3. timeout2添加到宏任务队列,待执行
    4. 将promise回调then2添加到微任务队列,待执行
    5. 执行promise,输出promise3,将回调then3添加到微任务队列中,将timeout in promise3添加到宏任务队列中
    6. 输出script end

宏任务:[ timeout1,timeout2,timeout in promise3 ];微任务:[ then2,then3 ]

  1. 依次读取并执行微任务队列:

    1. 输出then2
    2. 输出then3,将timeout in then3添加到宏任务队列中,待执行

宏任务:[ timeout1,timeout2,timeout in promise3,timeout in then3 ];微任务:无

  1. 依次读取并执行宏任务队列:

    1. 输出timeout1
    2. 输出timeout2,执行promise,输出promise1,将回调then1添加到微任务队列中

宏任务:[ timeout in promise3,timeout in then3 ];微任务:[ then1 ]

  1. 依次读取并执行微任务队列:

    1. 输出then1

宏任务:[ timeout in promise3,timeout in then3 ];微任务:无

  1. 依次读取并执行宏任务队列:

    1. 输出timeout in promise3
    2. 输出timeout in then3

需要注意: 这里的setTimeout中timeout参数都一致,如果是不同的参数,则按定时器的原理,定时短的先输出,但并不意味其优先级高。

简单总结一下执行的顺序: 执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。