宏任务与微任务执行顺序及对比记录

这篇具有很好参考价值的文章主要介绍了宏任务与微任务执行顺序及对比记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

一、 宏任务、微任务的基本概念

1.宏任务介绍

2.微任务介绍

3.宏任务、微任务发展及出现的原因:

 4.宏任务、微任务的基本类型

二、 事件循环模型(Event Loop)

三、 Promise、async和await 在事件循环中的处理

1.Promise:

        2. async/await :

这个知识点比较容易忽略,以为 await 回来后,直接继续执行后面的代码,这是不对的!

四、 process.nextTick在事件循环中的处理

五、 典型案例

5.1 典型的宏任务、微任务

5.2 宏任务中包含微任务

5.3 微任务中包含宏任务

5.4 async 宏任务

5.5 node 事件执行分析

5.6 综合案例

六、案例分析

6.1 典型的宏任务、微任务

6.2 宏任务中包含微任务

6.3 微任务中包含宏任务

6.4 async 宏任务

6.5 node 事件执行分析

6.6 综合案例

总结起来,宏任务与微任务的执行顺序可以归纳为以下几点:

综上所述,宏任务与微任务的执行顺序是宏任务队列执行完毕之后依次执行微任务队列,然后再从宏任务队列中取出下一个宏任务进行执行。在实际开发中,我们可以根据这个执行顺序来合理地安排任务的执行,以达到更好的效果。


前言

写此之前,也查阅了很多文章,并结合自己的理解,说说对Event Loop模型的理解、以及对Promiseasync/await在任务队列中的影响进行了分析,也给出了多种情形的任务案例以及分析解释,相信大家看完会有所收获;当然,也是自己的理解,难免有所偏差,欢迎大家指正~

一、 宏任务、微任务的基本概念

1.宏任务介绍

宏任务(Macro Task)是指由主线程上的事件触发器(Event Loop)进行调度的任务。宏任务包括但不限于如下几种情况:主线程上的代码块、setTimeout、setInterval、I/O 操作、DOM 事件等。

2.微任务介绍

微任务(Micro Task)是指由其他任务触发的任务。它们的优先级比宏任务更高,会在宏任务队列为空时立即执行。微任务包括但不限于如下几种情况:Promise 的回调函数、MutationObserver 的回调函数等。

3.宏任务、微任务发展及出现的原因:

JavaScript是典型的单线程(自上而下依次执行代码),但是,一个任务耗时过长,或多个任务需要执行时,势必导致线程阻塞,影响视图渲染效果。

在ES3以及以前的版本中,JavaScript本身没有异步任务能力,随着Promise的引入,JavaScript引擎自身也能够发起异步任务了。至此,JavaScript就可分为同步任务及异步任务;而JS又把异步任务做了进一步的划分,分为宏任务与微任务。

由于微任务执行快,一次性可以执行很多个,在当前宏任务执行后立刻清空微任务可以达到伪同步的效果,这对视图渲染效果起到至关重要的作用,这也是区分宏任务、微任务的原因。

 4.宏任务、微任务的基本类型

宏任务:

  • script(外层同步代码)

  • Ajax请求

  • setTimeout、setInterval

  • postMessage、MessageChannel

  • setImmediate(Node.js 环境)、I/O(Node.js 环境)

  • ...

微任务

  • Promise.then().catch() 和 .finally()

  • process.nextTick(Node.js 环境)

  • ...

二、 事件循环模型(Event Loop)

宏任务与微任务执行顺序及对比记录,js,前端,javascript

如上图,当同步代码执行完毕后,就会执行所有的宏任务,宏任务执行完成后,会判断是否有可执行的微任务;如果有,则执行微任务,完成后,执行宏任务;如果没有,则执行新的宏任务,形成事件循环。

这只是图示宏任务及微任务的执行关系,那么,js在 Event Loop中究竟是如何调用方法去处理的呢?

宏任务与微任务执行顺序及对比记录,js,前端,javascript

  1. 我们写的代码,js会进行任务类型分配,根据类型放入不同的任务队列中;
  2. Event Loop 会根据执行时机,将需要执行的函数,压入事件调用堆栈中执行;
  3. 相同的宏任务,也会有不同的执行时机,(类似 setTimeout 的 time),Event Loop 会控制将需要执行的函数压入

总结:Event Loop 在压入事件时,都会判断微任务队列是否还有需要执行的事件:如果有,则优先将需要执行的微任务压入;没有,则依次压入需要执行宏任务!

切记,宏任务执行完毕后,都会判断是否还有需要执行的微任务!!!在复杂的事件中,该点经常会错!!!

切记,宏任务执行完毕后,都会判断是否还有需要执行的微任务!!!在复杂的事件中,该点经常会错!!!

切记,宏任务执行完毕后,都会判断是否还有需要执行的微任务!!!在复杂的事件中,该点经常会错!!!

三、 Promise、async和await 在事件循环中的处理

1.Promise:

new Promise 创建实例的过程是同步的哦!

 console.log(1);
 new Promise((resolve, reject) => {
    console.log(2);
 })
 console.log(3);
// 结果: 1 2 3

但是,Promise.then().catch().finally()中的回调,是微任务。

 console.log(1);
 new Promise((resolve, reject) => {
    console.log(2);
    resolve(); // 触发 then 回调
    // reject(); // 触发 catch 回调
 }).then(()=>{
    console.log('then')
 }).catch(()=>{
    console.log('catch')
}).finally(()=>{
    console.log('finally')
})
 console.log(3);
// 结果: 1 2 3 then finally

宏任务与微任务执行顺序及对比记录,js,前端,javascript

上图是菜鸟教程-JavaScript Promise 中对Promise的讲解,我们想一下为啥要这样设计:

我们假设Promise 不是立即执行的,会有什么后果?利用Promise,多是封装异步请求(Ajax),而请求不是立即请求的,还需要等待Promise 任务执行,那么我们就失去了网络请求的时效性,会导致页面等待渲染(因为我们上面提及的 Event Loop 会根据事件执行时机,选择将事件压入堆栈中,我们无法保证自己写的【不是立即执行Promise】什么时候执行)。

        2. async/await :

简单来说,async是通过Promise包装异步任务。

async function fun1() {
  console.log('fun1 start')
  await fun2(); // 等待 fun2 函数执行完成
  console.log('fun1 end')
}
async function fun2() {
  console.log('fun2 start')
  console.log('fun2 end')
}
fun1()
 
// 输出结果: fun1 start、fun2 start、fun2 end、fun1 end

遇到 await 则需要等待 await 后的代码执行完成后,在往下执行代码。因此,可以将 await 看作抢夺线程的标记,fun1 中,本来是同步执行的,但是 await 的出现,导致线程执行了 fun2 的代码后,再次回到await,往后执行。

同时,await后面的代码,会进入then微任务中!!!

同时,await后面的代码,会进入then微任务中!!!

同时,await后面的代码,会进入then微任务中!!!

async function fun1() {
  console.log('fun1 start')
  await fun2(); // 等待 fun2 函数执行完成
  console.log('我是 await 后面的代码')
  console.log('fun1 end')
}
async function fun2() {
  console.log('fun2 start')
  console.log('fun2 end')
}
fun1()
console.log('await 阻塞,导致 await后面代码进入 then 微任务')

宏任务与微任务执行顺序及对比记录,js,前端,javascript

这个知识点比较容易忽略,以为 await 回来后,直接继续执行后面的代码,这是不对的!

四、 process.nextTick在事件循环中的处理

process.nextTick是Node环境的变量,process.nextTick() 是一个特殊的异步API,其不属于任何的Event Loop阶段。事实上Node在遇到这个API时,Event Loop根本就不会继续进行,会马上停下来执行process.nextTick(),这个执行完后才会继续Event Loop。所以,nextTick和Promise同时出现时,肯定是process.nextTick() 先执行。

可以类比 Vue.$nextTick(),也是需要执行完这个函数后,才能继续Event Loop。

五、 典型案例

5.1 典型的宏任务、微任务

setTimeout(function () {
    console.log('1');
});
 
new Promise(function (resolve) {
    console.log('2');
    resolve();
})
  .then(function () {
      console.log('3');
  })
  .then(function () {
      console.log('4')
  });
        
console.log('5');

5.2 宏任务中包含微任务

setTimeout(function () {
    console.log('1');
    new Promise(function (resolve) {
        console.log('2');
        resolve();
    })
      .then(function () {
          console.log('3');
      })
    console.log('4');
});
 
console.log('5');

5.3 微任务中包含宏任务

console.log('1');
new Promise(function (resolve) {
    console.log('2');
    resolve();
})
  .then(function () {
      console.log('3');
      setTimeout(function () {
          console.log('4');
       })
      console.log('5');
  })
 
console.log('6');

5.4 async 宏任务

async function fun1() {
  console.log('fun1 start')
  setTimeout(function () {
      console.log('fun1 setTimeout');
  });
  await fun2();
  console.log('fun1 end')
}
 
async function fun2() {
  console.log('fun2 start')
  new Promise((resolve)=>{
      console.log('fun2 Promise')
      resolve()
  })
    .then(()=>{
        console.log('fun2 Promise then')
    })
  console.log('fun2 end')
}
 
fun1()

5.5 node 事件执行分析

console.log('1');
 
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
 
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

5.6 综合案例

console.log('script start')
 
async function fun1() {
  console.log('fun1 start')
  process.nextTick(function() {
    console.log('fun1 process nextTick');
  })
  setTimeout(function () {
      console.log('fun1 setTimeout');
      new Promise(function (resolve) {
          console.log('fun1 Promise');
          resolve();
      })
        .then(function () {
            console.log('fun1 Promise then');
            setTimeout(function () {
                console.log('fun1 Promise then setTimeout');
             })
            console.log('fun1 Promise then end');
        })
  });
  await fun2();
  console.log('fun1 end')
}
 
async function fun2() {
  console.log('fun2 start')
  setTimeout(function () {
      console.log('fun2 setTimeout');
  });
  new Promise((resolve)=>{
      console.log('fun2 Promise')
      resolve()
  })
    .then(()=>{
        console.log('fun2 Promise then')
    })
  console.log('fun2 end')
}
 
fun1()
 
setTimeout(function() {
  console.log('setTimeout-000')
}, 0)
 
new Promise(resolve => {
  console.log('Promise')
  process.nextTick(function() {
    console.log('Promise process nextTick');
  })
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
    process.nextTick(function() {
      console.log('promise2 process nextTick');
    })
  })
 
console.log('script end')

六、案例分析

6.1 典型的宏任务、微任务

 // 同步任务
 console.log('2'); // new promise 实例化过程
 console.log('5');
 // 
 // 将 setTimeout console.log('1'); 放入宏任务队列
 // 将Promise 的回调放入微任务队列
 // then  console.log('3');
 // then  console.log('4')
 
 // 先微任务
 console.log('3');
 console.log('4')
// 再宏任务
 console.log('1');
 
// 因此,输出结果: 2 5 3 4 1

6.2 宏任务中包含微任务

// 同步任务
console.log('5');
// 将 setTimeout 放入宏任务队列,此时,没有微任务执行,因此,开始执行setTImeout宏任务
console.log('1');
// new Promise 实例化 同步执行:
console.log('2');
// 将Promise.then 回调放入微任务
// 当前(setTimeout)的宏任务事件还没有执行完!!!
// 注意哈!!当前(setTimeout)的宏任务事件还没有执行完!!!,事件未跳出当前 Loop
console.log('4');
// 执行完宏任务,开始执行 微任务
console.log('3');
 
// 因此,结果为: 5 1 2 4 3

6.3 微任务中包含宏任务

 // 同步代码:
console.log('1');
// new Promise 
console.log('2');
console.log('6');
// 微任务:Promise.then
console.log('3');
console.log('5');
// 结束当前 Loop[有上个例子,这个就不难理解了]
// 开启宏任务
console.log('4');
 
// 因此,结果为:1 2 6 3 5 4

6.4 async 宏任务

 // fun1
console.log('fun1 start')
// 将setTimeout 放入宏任务队列
//  await fun2(); 进入 fun2
console.log('fun2 start')
//   new Promise
console.log('fun2 Promise')
// 将 then 放入微任务
console.log('fun2 end') // 当前任务队列
 
// 有微任务,先执行微任务
console.log('fun2 Promise then')
// 回到 await 处
console.log('fun1 end') // 当前 fun1 队列
console.log('fun1 setTimeout'); // 最后的宏任务

6.5 node 事件执行分析

宏任务与微任务执行顺序及对比记录,js,前端,javascript

// 从上往下:
console.log('1'); // 同步代码
//  setTimeout 宏任务1
//  process.nextTick 微任务1
console.log('7'); // new Promise
// Promise.then 微任务2
// setTimeout 宏任务2
// -- 开始执行微任务
console.log('6');
console.log('8')
 
// -- 开始宏任务1 
console.log('2');
//  process.nextTick 微任务!!!
console.log('4'); // new Promise
// Promise.then 微任务!!!
// 到此,当前宏任务已执行完毕,有微任务,需要先执行微任务
console.log('3');
console.log('5')
        
// 执行宏任务 2
console.log('9');
//  process.nextTick 微任务
console.log('11');// new Promise
// Promise.then 微任务!!!
console.log('10');
console.log('12')
 
// 因此,结果为:1 7 6 8 2 4 3 5 9 11 10 12

6.6 综合案例

宏任务与微任务执行顺序及对比记录,js,前端,javascript

// 这个案例,就应用了 await 导致的 then 微任务细节,我第一次分析也错了
// 还涉及了process.nextTick node 的执行时机优先
         
// 开始分析:
        console.log('script start')
        // fun1() 进入 fun1
        console.log('fun1 start')
        // 生成微任务 process.nextTick(fun1)
        // 生成 宏任务 setTimeout (fun1)
        await fun2(); // 进入 fun2
        console.log('fun2 start')
        // 生成宏任务 setTimeout (fun2)
        console.log('fun2 Promise') // new Promise
        // 生成 Promise.then 微任务
        console.log('fun2 end')
        // !!!此时,fun2 已经有返回值了,不需要等待 fun2 中的事件执行,回到 await 处,被标记了 await .then 的微任务
        // 因此,执行主任务
        console.log('Promise') // new Promise
        // 生成 Promise process.nextTick 微任务
        // 生成 Promise1.then 微任务
        // 生成 Promise2.then 微任务
        console.log('script end')
        /**
         * 分析微任务队列
         * 1. 第一个微任务:process.nextTick(fun1)
         * 2. fun2 Promise.then // 容易漏
         * 3. await .then 的微任务 !!!!!!!!!!!!!
         * 4. Promise process.nextTick 微任务
         * 5. Promise1.then 微任务
         * 6. Promise2.then 微任务
         *
        */
        // 根据 Node process 优先级,先执行 process
        console.log('fun1 process nextTick');
        console.log('Promise process nextTick');
        console.log('fun2 Promise then')
        // await.then微任务[await 后的所有代码,如果还有任务,具体再分析即可]
        console.log('fun1 end')
        console.log('promise1')
        console.log('promise2') // 执行到这,又生成新的 process.nextTick 微任务,又先执行
        console.log('promise2 process nextTick');
        // 没有微任务了,开始执行宏任务
        console.log('fun1 setTimeout');
        console.log('fun1 Promise'); // 生成新的 promise.then 微任务,当前宏任务已执行完成,开始执行微任务
        console.log('fun1 Promise then'); // 生成新的 宏任务 fun1 Promise then setTimeout
        console.log('fun1 Promise then end');
        /**
         * 此时,分析宏任务队列
         * 1.  第一个 是 fun2 setTimeout
         * 2.  setTimeout(function () { console.log('setTimeout-000') }, 0)
         * 3.  fun1 Promise then setTimeout
         * */
        // 因此, 依次执行宏任务
        console.log('fun2 setTimeout');
        console.log('setTimeout-000')
        console.log('fun1 Promise then setTimeout');

这个案例比较复杂,某些事件容易漏掉,因此,建议大家手动勾起来,每一个事件都对应到事件队列中,这个案例,考察两个点,一个是 await的处理及 node.process 的优先级。大家弄懂这个案例,出去面试,手撕这种题目应该不是问题了。

总结起来,宏任务与微任务的执行顺序可以归纳为以下几点:

  1. 当一个宏任务执行完毕后,会检查微任务队列中是否存在微任务。
  2. 如果存在微任务,则会依次执行微任务直到微任务队列为空。
  3. 然后再从宏任务队列中取出下一个宏任务进行执行。
  4. 这个过程会一直重复,直到宏任务队列和微任务队列都为空。

在实际开发中,我们常常会利用宏任务与微任务的执行顺序来进行任务的调度。通过将一些耗时较长的任务放在宏任务中,可以保证其他任务的及时执行;而将一些需要优先执行的任务放在微任务中,可以保证其优先级更高。

需要注意的是,宏任务与微任务的执行顺序是由浏览器的事件循环机制控制的,不同的浏览器可能存在一些差异。因此,在实际开发中,我们应该合理地安排宏任务与微任务的使用,避免出现一些不可预期的结果。

综上所述,宏任务与微任务的执行顺序是宏任务队列执行完毕之后依次执行微任务队列,然后再从宏任务队列中取出下一个宏任务进行执行。在实际开发中,我们可以根据这个执行顺序来合理地安排任务的执行,以达到更好的效果。

参考文章:

宏任务和微任务的执行顺序_微任务和宏任务的执行顺序-CSDN博客

宏任务与微任务执行顺序(超详细讲解)_宏任务和微任务谁先执行-CSDN博客

微任务和宏任务哪个先执行?_宏任务和微任务谁先执行-CSDN博客

例子出处:宏任务与微任务执行顺序(超详细讲解)_宏任务和微任务谁先执行-CSDN博客文章来源地址https://www.toymoban.com/news/detail-812511.html

到了这里,关于宏任务与微任务执行顺序及对比记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 20230515学习笔记——js中的同步任务与异步任务,宏任务与微任务

    2023-05-15 (1)js中的同步任务与异步任务 ①同步任务是指:不耗时的任务,就是执行很快, ②异步任务是指:耗时的任务,它里面有一个机制是EventLoop(事件循环),即值耗时任务会被js分配到宿主环境中进行执行,执行后的结果放到一个“消息队列”中,当js将同步任务执行完

    2024年02月04日
    浏览(42)
  • 【案例讲解】LVGL 向UI任务发送顺序执行的延时任务方法

    更多源码分析请访问: LVGL 源码分析大全 由于 lv_timer_create 的执行实现方式为 后进先出 的栈执行方式,这导致在添加一些需要顺序执行的延时任务时会很麻烦,因此,需要实现一个 先进先出 接口。 以ESP32中 examples/factory_demo 为例:ESP32上的LVGL是采用多任务(相当于Linux上的

    2024年01月16日
    浏览(40)
  • 【昕宝爸爸小模块】如何让Java的线程池顺序执行任务

    ➡️博客首页       https://blog.csdn.net/Java_Yangxiaoyuan        欢迎优秀的你👍点赞、🗂️收藏、加❤️关注哦。        本文章CSDN首发,欢迎转载,要注明出处哦!        先感谢优秀的你能认真的看完本文,有问题欢迎评论区交流,都会认真回复! Java中的线程池

    2024年01月16日
    浏览(45)
  • js执行顺序

    1.首先js是单线程的 所有的同步任务都是按顺序依次执行的,前面的执行完了之后才会执行后面的任务。遇到计算量大的代码就会阻塞在这里。下面的代码就是打印输出10000次,当循环走完了之后 才会走后面的代码,输出的时间是981ms 输出981ms 2.因为js是单线程的,所有的任务

    2023年04月08日
    浏览(38)
  • AJAX:宏任务与微任务

    异步任务划分为了     宏任务:由浏览器环境执行的异步代码   微任务:由 JS 引擎环境执行的异步代码 宏任务和微任务具体划分: 左边表格是宏任务,右边是微任务 事件循环模型 执行过程: 1-JS引擎把 console.log(1) 放入调用栈中,执行后弹出 2-JS引擎发现 setTimeout(…, 0) 是

    2024年02月16日
    浏览(43)
  • 【转】JavaScript 执行上下文——JS 的幕后工作原理

    转自译文: JavaScript 执行上下文——JS 的幕后工作原理 。 译文中图片不显示,要结合原文看,看着不方便。整理了一份含图片的。所以有了此篇的转载,以方便阅读。 以下是正文: 原文:JavaScript Execution Context – How JS Works Behind The Scenes,作者:Victor Ikechukwu 所有JavaScript代码

    2024年01月20日
    浏览(43)
  • 【技巧】ScriptEngine--Java动态执行JS Javascript脚本(可调用java的方法)

    开发手册 Java Platform, Standard Edition Nashorn User\\\'s Guide, Release 14 用户手册 Nashorn User\\\'s Guide (oracle.com) https://docs.oracle.com/en/java/javase/14/nashorn/ 甚至可以让前端来写后端业务代码 可以通过js调用java的方法, 通过传参的方式将java对象传给js 可以单独写个JsUtil 让js来调用 里边写常用的方法

    2024年02月01日
    浏览(48)
  • jQuery.js - 前端必备的Javascript库

    作者: WangMin 格言: 努力做好自己喜欢的每一件事 jQuery.js 是什么? jQuery是一个快速简洁、免费开源易用的JavaScript框架, 倡导写更少的代码,做更多的事情 。它封装JavaScript常用的功能代码,提供了一种简便的JavaScript设计模式,以及我们开发中常用到的操作DOM的API,优化HTML文

    2024年02月05日
    浏览(77)
  • web前端框架JS学习之JavaScript类型转换

    vascript有多种数据类型,如字符串、数字、布尔等,可以通过typeof语句来查看变量的数据类型。数据类型转换就是数据类型之间相互转换,比如把数字转成字符串、把布尔值转成字符串、把字符串转成数字等,这在工作也是经常碰到的。 本期我们就给大家说说web前端框架JS学

    2024年02月10日
    浏览(64)
  • 【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    🐚 作者: 阿伟 💂 个人主页: Flyme awei 🐋 希望大家多多支持😘一起进步呀! 💬 文章对你有帮助👉关注✨点赞👍收藏📂 第一种: var 数组名 = new Array(); 创建一个空数组 第二种: var arr2 = new Array(10); 创建一个定长为10的数组 第三种 var arr3 = new Array(a,b,c); 创建时直接指定元素值

    2023年04月08日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包