JS中的Event loop、宏队列与微队列

JS 是门非阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,我们在多个线程中处理 DOM 就可能会发生问题(一个线程中新加节点,另一个线程中删除节点),当然可以引入读写锁解决这个问题。参考
所以需要对异步代码进行特殊的处理才行

macrotask(宏任务)

在浏览器端,其可以理解为该任务执行完后,在下一个macrotask执行开始前,浏览器可以进行页面渲染。(对应了上一篇文章讲的script代码执行完成后,在执行下一段script代码前,页面渲染了之前的dom:链接)
触发macrotask任务的操作包括:

  • dom事件回调,
  • ajax回调,
  • 定时器回调,
  • script(整体代码),
  • setTimeout、setInterval、setImmediate
  • I/O、UI交互事件
  • postMessage、MessageChannel

microtask(微任务)

可以理解为在macrotask任务执行后,页面渲染前立即执行的任务。
触发microtask任务的操作包括:

  • Promise回调:Promise.then
  • Mutation回调:MutationObserver
  • process.nextTick(Node环境)

参考

JS中用来存储待执行回调函数的队列包含2个不同特定的列队

  • 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调/DOM事件回调/ajax回调
  • 微列队:用来保存待执行的微任务(回调),比如:promise的回调/MutationObserver的回调

Event loop的执行顺序

  1. 执行同步代码,这属于宏任务 ,遇到异步代码,需要判断是放入宏任务队列还是微任务队列
  2. 执行栈为空,查询是否有微任务需要执行
  3. 执行所有微任务
  4. 必要的话渲染 UI
  5. 然后开始下一轮 Eventloop,执行宏任务中的异步代码(取出宏队列中排队的宏任务执行)

例子1

console.log('script start')

setTimeout(function() {
     
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
     
  console.log('Promise')
  resolve()
})
  .then(function() {
     
    console.log('promise1')
  })
  .then(function() {
     
    console.log('promise2')
  })

console.log('script end')
// script start => Promise => script end => promise1 => promise2 => setTimeout

结果:
JS中的Event loop、宏队列与微队列_第1张图片
分析:
以上代码执行步骤如下

  1. 第一个宏任务:执行所有同步代码
  2. 打印输出script start
  3. 遇到setTimeout,这算异步,而且是宏任务,所以把setTimeout放入宏队列排队
  4. 遇到Promise,但是Promise的参数里面的函数不是异步的,所以会打印输出’Promise’,然后把Promise的后续回调then放入微队列排队
  5. Promise的第二个回调,继续放入微队列排队
  6. 打印输出script end
  7. 这一个宏任务完成,依次执行当前微队列里面的微任务
  8. 微任务1,打印输出promise1
  9. 微任务2,打印输出promise2
  10. 微任务执行完毕,进入宏队列取出下一个宏任务
  11. 第二个宏任务开始执行
  12. 输出setTimeout

最终答案:script start => Promise => script end => promise1 => promise2 => setTimeout

例子2

setTimeout(function(){
     console.log(1)},0);
new Promise(function(resolve){
     
    console.log(2)
    for( var i=0 ; i<10000 ; i++ ){
     
        i==9999 && resolve()
    }
    console.log(3)
}).then(function(){
     
    console.log(4)
});
console.log(5);
// 这的问题是,为什么答案是 2 3 5 4 1
// 而不是 2 3 5 1 4

JS中的Event loop、宏队列与微队列_第2张图片
分析:

  1. 第一个宏任务:执行所有同步代码
  2. 遇到setTimeout,是宏任务,把setTimeout放入宏队列排队
  3. 遇到Promise,但是Promise的参数里面的函数不是异步的,所以会打印输出’2’,然后打印’3’,然后把Promise的后续回调then放入微队列排队
  4. 打印输出5
  5. 这一个宏任务完成,依次执行当前微队列里面的微任务
  6. 微任务,打印输出4
  7. 微任务执行完毕,进入宏队列取出下一个宏任务
  8. 第二个宏任务开始执行
  9. 输出1

你可能感兴趣的:(面试,总结帖,javascript)