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 相关文章推荐
JS小功能(列表页面隔行变色)简单实现
Nov 28 Javascript
node.js [superAgent] 请求使用示例
Mar 13 Javascript
JavaScript学习总结之JS、AJAX应用
Jan 29 Javascript
详解JavaScript异步编程中jQuery的promise对象的作用
May 03 Javascript
AnjularJS中$scope和$rootScope的区别小结
Sep 18 Javascript
Angular之指令Directive用法详解
Mar 01 Javascript
jquery中关于bind()方法的使用技巧分享
Mar 30 jQuery
JavaScript对JSON数据进行排序和搜索
Jul 24 Javascript
Vue中"This dependency was not found"问题的解决方法
Jun 19 Javascript
玩转Koa之koa-router原理解析
Dec 29 Javascript
JS基于ES6新特性async await进行异步处理操作示例
Feb 02 Javascript
jquery 时间戳转日期过程详解
Oct 12 jQuery
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
Linux系统下使用XHProf和XHGui分析PHP运行性能
2015/12/08 PHP
作为程序员必知的16个最佳PHP库
2015/12/09 PHP
php使用str_shuffle()函数生成随机字符串的方法分析
2017/02/17 PHP
php文件包含目录配置open_basedir的使用与性能详解
2017/04/03 PHP
Javascript Cookie读写删除操作的函数
2010/03/02 Javascript
使用jQuery避免鼠标双击的解决方案
2013/08/21 Javascript
JavaScript实现多个重叠层点击切换效果的方法
2015/04/24 Javascript
JavaScript中的toUTCString()方法使用详解
2015/06/12 Javascript
ajax接收后台数据在html页面显示
2017/02/19 Javascript
详解Angular2表单-模板驱动的表单(Template-Driven Forms)
2017/08/04 Javascript
基于小程序请求接口wx.request封装的类axios请求
2020/07/02 Javascript
微信小程序组件生命周期的踩坑记录
2021/03/03 Javascript
[15:20]DOTA2亚洲邀请赛总决赛开幕式表演:羽泉献唱
2017/04/05 DOTA
从Python程序中访问Java类的简单示例
2015/04/20 Python
Python模块搜索路径代码详解
2018/01/29 Python
Python基于更相减损术实现求解最大公约数的方法
2018/04/04 Python
基于Python打造账号共享浏览器功能
2019/05/30 Python
解决django 向mysql中写入中文字符出错的问题
2020/05/18 Python
Django 实现 Websocket 广播、点对点发送消息的代码
2020/06/03 Python
HTML5在线预览PDF的示例代码
2017/09/14 HTML / CSS
全球航班旅行搜索网站:Cheapflights
2017/05/19 全球购物
美国精品地毯网站:Boutique Rugs
2020/03/04 全球购物
逻辑链路控制协议
2016/10/01 面试题
应届生幼儿园求职信
2013/11/12 职场文书
幼儿园教师考核制度
2014/02/01 职场文书
争论的故事教学反思
2014/02/06 职场文书
最新奶茶店创业计划书范文
2014/02/08 职场文书
教师试用期自我鉴定
2014/02/12 职场文书
美国留学经济担保书
2014/05/20 职场文书
法律进社区活动总结
2015/05/07 职场文书
公司2015年终工作总结
2015/05/26 职场文书
普希金的诗歌赏析(3首)
2019/08/20 职场文书
Python代码,能玩30多款童年游戏!这些有几个是你玩过的
2021/04/27 Python
Python基础数据类型tuple元组的概念与用法
2021/08/02 Python
JavaScript文档对象模型DOM
2021/11/20 Javascript
Pandas实现批量拆分与合并Excel的示例代码
2022/05/30 Python