Javascript中Microtask和Macrotask鲜为人知的知识点


Posted in Javascript onApril 02, 2022

首先我们来看一道题目,如下javascript代码,执行后会在控制台打印出什么内容?

async function async1() {
   console.log('async1 start');
   await async2();
   console.log('async1 end');
 }
 async function async2() {
   console.log('async2 start');
   return new Promise((resolve, reject) => {
     resolve();
     console.log('async2 promise');
   })
 }
 console.log('script start');
 setTimeout(function() {
   console.log('setTimeout');
 }, 0);  
 async1();
 new Promise(function(resolve) {
   console.log('promise1');
   resolve();
 }).then(function() {
   console.log('promise2');
 }).then(function() {
   console.log('promise3');
 });
 console.log('script end')

说实话,真正能在面试中把这道题目答对的前端工程师凤毛麟角。我们先来瞧一下答案吧。把以上代码存到test.js文件中,并用node执行一下,结果如下:

Javascript中Microtask和Macrotask鲜为人知的知识点

如果把以上代码贴到一个网页中的script标签里面,然后打开这个网页,再打开控制台,可以看到如下输出(Chrome 64位 63.0.3239.84):

Javascript中Microtask和Macrotask鲜为人知的知识点

结果和node打印的一模一样。那么为什么是这个顺序呢?

我们都知道js的单线程特性(html5的web worker不算在内~)以及良好的异步支持。在单线程的前提下,异步任务到底什么时候开始执行,其实是有两个队列来进行管理,即Macrotask和Microtask(只有一个字母的差距,不要认错……)。在当前正在执行的线程中,如果碰到属于Macrotask的异步任务,则放入Macrotask队列;碰到Microtask的异步任务则放入Microtask队列。注意这里只是把任务放入队列,并不会执行它。等到当前主线程任务执行完毕之后,会依次从Microtask队列中取出任务执行,在执行期间当然还是遵循碰到异步任务放入相应队列的原则。等到Microtask任务全部执行过了,此时再从Macrotask队列中取出一个任务执行。

属于Macrotask的任务有:

setTimeout,setInteveral,script标签,I/O,UI渲染

属于Microtask的任务有:

Promise,async/await,process.nextTick,Object.observe,MutationObserver

(事实上,即使同样是Microtask,内部也是有优先级的差别的,例如NodeJS的实现上,process.nextTick比Promise要先执行。相关问题可以瞧瞧这个连接:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 。反正我瞧到一半就放弃了,好在async/await和Promise没有优先级差别)

然后我们来分析一下本题中的执行顺序:

【1】第15行执行,打印出script start

【2】第16至18行,把回调任务放入Macrotask (目前Macrotask:第16行setTimeout,Microtask:空)

【3】第20行,执行async1函数,先打印出第2行的async1 start

【4】第3行的async2先执行,打印出第8行的async2 start

【5】第9行至第12行遇到Promise,先打印出第11行的async2 promise(注意不管你resolve写在new Promise的函数什么位置,都跟写到最后一句一样!)

【6】第3行的async2返回了Promise,并且async2前面有await修饰,因此后面第4行的任务被放到Microtask(目前Macrotask:第16行setTimeout,Microtask:第4行)

【7】第22至25行,打印出promise1,并把第26行放入Microtask,注意第28行还没执行到,所以这行什么都不做(目前Macrotask:第16行setTimeout,Microtask:第4行,第26行)

【8】第30行打印script end(目前Macrotask:第16行setTimeout,Microtask:第4行,第26行)

【9】脚本主线程执行结束,现在拿出来一个Microtask,即第4行,打印async1 end(目前Macrotask:第16行setTimeout,Microtask:第26行)

【10】再拿出来一个Microtask,即第26行,打印promise2,此时由于第26行后面跟着then,所以把第28行插入Microtask(目前Macrotask:第16行setTimeout,Microtask:第28行)

【11】再拿出来一个Microtask,即第28行,打印promise3(目前Macrotask:第16行的setTimeout,Microtask:空)

【12】Microtask没有了,执行下一个Macrotask,即第16行的setTimeout,打印setTimeout,结束

需要注意的是,以下两种写法,效果是一模一样的(resolve的位置无所谓):

写法1:
new Promise((resolve, reject) => {
  console.log('1111');
  resolve();
  console.log('2222');
});
 
写法2:
new Promise((resolve, reject) => {
  console.log('1111');
  console.log('2222');
  resolve();
});

另外,对于Promise的链式调用,如new Promise(....).then(...).then(...)....,一次只放第一个then的内容进入Microtask,等第一个then执行的时候,会把第二个then放入Microtask,而不是一次把两个then都放进去。

以上就是Javascript中Microtask和Macrotask鲜为人知的知识点的详细内容,更多关于Javascript中Microtask和Macrotask的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
javascript入门·图片对象(无刷新变换图片)\滚动图像
Oct 01 Javascript
js实现完全自定义可带多级目录的网页鼠标右键菜单方法
Feb 28 Javascript
分享有关jQuery中animate、slide、fade等动画的连续触发、滞后反复执行的bug
Jan 10 Javascript
jQuery  ready方法实现原理详解
Oct 19 Javascript
Angular2学习笔记——详解路由器模型(Router)
Dec 02 Javascript
浅谈javascript的闭包
Jan 23 Javascript
vue-router配合ElementUI实现导航的实例
Feb 11 Javascript
快速解决bootstrap下拉菜单无法隐藏的问题
Aug 10 Javascript
JS实现的图片选择顺序切换和循环切换功能示例【测试可用】
Dec 28 Javascript
layer弹出层显示在top顶层的方法
Sep 11 Javascript
jQuery实现简单日历效果
Jul 05 jQuery
html中创建并调用vue组件的几种方法汇总
Nov 17 Javascript
vue中div禁止点击事件的实现
Apr 02 #Vue.js
Vue组件更新数据v-model不生效的解决
Apr 02 #Vue.js
Vue+TypeScript中处理computed方式
Apr 02 #Vue.js
Vue+Flask实现图片传输功能
Apr 01 #Vue.js
在vue中import()语法不能传入变量的问题及解决
Apr 01 #Vue.js
Vue中使用import进行路由懒加载的原理分析
Apr 01 #Vue.js
angular异步验证器防抖实例详解
You might like
PHP+FLASH实现上传文件进度条相关文件 下载
2007/07/21 PHP
如何使用PHP对网站验证码进行破解
2015/09/17 PHP
PHP中对数组的一些常用的增、删、插操作函数总结
2015/11/27 PHP
Yii2简单实现多语言配置的方法
2016/07/23 PHP
PHP使用PDO操作sqlite数据库应用案例
2019/03/07 PHP
PHP命名空间定义与用法实例分析
2019/08/14 PHP
jquery入门—数据删除与隔行变色以及图片预览
2013/01/07 Javascript
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
2013/01/24 Javascript
jQuery+CSS 半开折叠效果原理及代码(自写)
2013/03/04 Javascript
JavaScript基础知识学习笔记
2014/12/02 Javascript
js生成验证码并直接在前端判断
2015/05/15 Javascript
jquery实现带缩略图的可定制高度画廊效果(5种)
2015/08/28 Javascript
js使用i18n实现页面国际化的方法
2017/05/09 Javascript
Vue中多个元素、组件的过渡及列表过渡的方法示例
2019/02/13 Javascript
JS基于开关思想实现的数组去重功能【案例】
2019/02/18 Javascript
vue elementui 实现搜索栏公共组件封装的实例代码
2020/01/20 Javascript
vue中element 的upload组件发送请求给后端操作
2020/09/07 Javascript
pymssql数据库操作MSSQL2005实例分析
2015/05/25 Python
Python3.4学习笔记之类型判断,异常处理,终止程序操作小结
2019/03/01 Python
pandas进行时间数据的转换和计算时间差并提取年月日
2019/07/06 Python
Python调用Windows API函数编写录音机和音乐播放器功能
2020/01/05 Python
全面介绍python中很常用的单元测试框架unitest
2020/12/14 Python
python中numpy.empty()函数实例讲解
2021/02/05 Python
纯CSS3实现漂亮的input输入框动画样式库(Text input love)
2018/12/29 HTML / CSS
美国珠宝店:Helzberg Diamonds
2018/10/24 全球购物
教师求职推荐信范文
2013/11/20 职场文书
个性发展自我评价
2014/02/11 职场文书
电子商务专业毕业生自荐书
2014/06/22 职场文书
计生工作先进事迹
2014/08/15 职场文书
党员个人整改方案及措施
2014/10/25 职场文书
mybatis调用sqlserver存储过程返回结果集的方法
2021/05/08 SQL Server
MySQL子查询中order by不生效问题的解决方法
2021/08/02 MySQL
MySQL如何解决幻读问题
2021/08/07 MySQL
django 认证类配置实现
2021/11/11 Python
div与span之间的区别与使用介绍
2021/12/06 HTML / CSS
Springboot-cli 开发脚手架,权限认证,附demo演示
2022/04/28 Java/Android