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 相关文章推荐
为jquery.ui.dialog 增加“自动记住关闭时的位置”的功能
Nov 24 Javascript
javascript 正则替换 replace(regExp, function)用法
May 22 Javascript
js获得地址栏?问号后参数的方法
Aug 08 Javascript
JavaScript位置与大小(1)之正确理解和运用与尺寸大小相关的DOM属性
Dec 26 Javascript
jQuery链式调用与show知识浅析
May 11 Javascript
JS与jQuery实现隔行变色的方法
Sep 09 Javascript
前端设计师们最常用的JS代码汇总
Sep 25 Javascript
JavaScript实现QQ列表展开收缩扩展功能
Oct 30 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
Sep 21 Javascript
JavaScript 点击触发复制功能实例详解
Nov 02 Javascript
详解vue-cli3多环境打包配置
Mar 28 Javascript
layui type2 通过url给iframe子页面传值的例子
Sep 06 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
一键删除顽固的空文件夹 软件下载
2007/01/26 PHP
codeigniter框架批量插入数据
2014/01/09 PHP
php实现微信支付之现金红包
2018/05/30 PHP
高亮显示web页表格行的javascript代码
2010/11/19 Javascript
关于用Jquery的height()、width()计算动态插入的IMG标签的宽高的问题
2010/12/08 Javascript
简介JavaScript中的push()方法的使用
2015/06/09 Javascript
基于AngularJS+HTML+Groovy实现登录功能
2016/02/17 Javascript
Angular.JS中的指令引用template与指令当做属性详解
2017/03/30 Javascript
全面解析vue router 基本使用(动态路由,嵌套路由)
2018/09/02 Javascript
原生js实现淘宝放大镜效果
2020/10/28 Javascript
jQuery实现网页拼图游戏
2020/04/22 jQuery
vue中img src 动态加载本地json的图片路径写法
2019/04/25 Javascript
BootStrap前端框架使用方法详解
2020/02/26 Javascript
JS数据类型判断的几种常用方法
2020/07/07 Javascript
Python 列表(List)操作方法详解
2014/03/11 Python
Python解惑之True和False详解
2017/04/24 Python
python实现数据图表
2017/07/29 Python
Python使用回溯法子集树模板获取最长公共子序列(LCS)的方法
2017/09/08 Python
Python除法之传统除法、Floor除法及真除法实例详解
2019/05/23 Python
numpy.array 操作使用简单总结
2019/11/08 Python
基于python调用psutil模块过程解析
2019/12/20 Python
python自动识别文本编码格式代码
2019/12/26 Python
Python实现密钥密码(加解密)实例详解
2020/04/26 Python
使用HTML5 Canvas绘制直线或折线等线条的方法讲解
2016/03/14 HTML / CSS
html5 viewport使用方法示例详解
2013/12/02 HTML / CSS
html5的canvas元素使用方法介绍(画矩形、画折线、圆形)
2014/04/14 HTML / CSS
音乐教学随笔感言
2014/02/19 职场文书
《在山的那边》教学反思
2014/02/23 职场文书
高中生职业规划范文
2014/03/09 职场文书
中医学专业自荐信范文
2014/04/01 职场文书
城市规划应届毕业生自荐信
2014/07/04 职场文书
公务员个人总结
2015/02/12 职场文书
2015年度优秀员工自荐书
2015/03/06 职场文书
2016年度优秀辅导员事迹材料
2016/02/26 职场文书
MySQL单表千万级数据处理的思路分享
2021/06/05 MySQL
腾讯云服务器部署前后分离项目之前端部署
2022/06/28 Servers