【JavaScript】宏任务与微任务

之前我们谈到过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)。

猜你喜欢

转载自blog.csdn.net/weixin_43790653/article/details/123781431
今日推荐