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

首先我们来看一道题目,如下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执行一下,结果如下:

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

结果和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的资料请关注我们其它相关文章!

(0)

相关推荐

  • JS常用知识点整理

    1.CORS CORS(Corss-Origin Resource Sharing,跨资源共享),基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应的成功或失败.即给请求附加一个额外的Origin头部,其中包含请求页面的源信息(协议.域名和端口),以便服务器根据这个头部决定是否给予响应. 2.document.domain 将页面的document.domain设置为相同的值,页面间可以互相访问对方的JavaScript对象. 注意: 不能将值设置为URL中不包含的域

  • javascript面向对象程序设计实践常用知识点总结

    本文实例讲述了javascript面向对象程序设计实践常用知识点.分享给大家供大家参考,具体如下: 实践一:原型中的引用类型的属性是共享的 var Person = function(){}; Person.prototype = { info:{ "name":"Tom" } } var p1 = new Person(); var p2 = new Person(); p1.info.name = '我是p1'; p2.info.name = '我是p2'; c

  • 关于JS模块化的知识点分享

    模块化是一个语言膨胀的必经之路,它能够帮助开发者拆分和组织代码. Module模式 在模块化规范形成之前,JS开发者使用Module设计模式来解决JS全局作用域的污染问题.Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法.在JavaScript中,Module模式使用匿名函数自调用 (闭包)来封装,通过自定义暴露行为来区分私有成员和公有成员. let myModule = (function (window) { let moduleName = 'module' /

  • JavaScript 总结几个提高性能知识点(推荐)

    前段时间花时间看了大半的<High Performance JavaScript>这本书啊,然后就开始忙项目了,庆幸最忙的一周已经熬过去了.由于空不出时间,这个月写的学习笔记也不多,忙完最苦X的一周,这两天晚上也算是挑灯夜读了...终于是在残血之际将这本书shut down了... 既然读完了,总归是要学到些什么的.说说对这本书的看法先吧,整体的来说,内容还是不错的,就是感觉有点老了(作为前端小白,也可能是自身水平有限,未能体会到其中真意).看这本书的过程中也是写了挺多代码用以测试的,并且对本

  • javascript 易错知识点实例小结

    本文实例总结了javascript 易错知识点.分享给大家供大家参考,具体如下: 为什么 typeof null === 'object' 原理是这样的,不同的对象在底层都表示为二进制,在JavaScript中二进制前三位都为0的话会被判断为 object 类型, null 的二进制表示是全0,自然前三位也是0,所以执行 typeof 时会返回" object ". 对象属性的存在性 如 myObject.a 的属性访问返回值可能是 undefined ,但是这个值有可能是属性中存储的

  • javascript知识点详解

    目录 一.JavaScript基本介绍 二.Javascript基础语法 三.JavaScript事件 例子2:当点击div标签时,做一件事,如点击后弹出对话框.--用id类 例子3:添加标签p,用class类来 四. Javascript书写位置(引入方式) 4.1 内嵌js 4.2 外链js 4.3 行内js(禁止使用) 五. js常见特效(了解) 5.1 导航跟随/电梯导航 5.2 返回顶部 5.3 滚动跟随 5.4 呼吸灯/焦点图(banner图) 5.5 js实现动画效果 5.6 模态

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

    首先我们来看一道题目,如下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();

  • JavaScript中0、空字符串、'0'是true还是false的知识点分享

    最近被问到关于js中空字符串是true还是false得问题,一时间没想起来,现在在chrome的console面板上输出代码测试一下. "" == false 结果是true   在js中空字符串与0相等  即空字符串等于false 0 == false  结果是tue   比较的时候 布尔类型的转换成number类型  false 为0  true 为1 "0" == fase 结果是true  字符串准成number Boolean("0")

  • javascript中replace( )方法的使用

    最近在浏览一些阿里的前端面试题,其中有一题涉及了javascript中replace( )方法的使用,下面是原题: "说出以下函数的作用是?空白区域应该填写什么?" // define (function (window) { function fn(str) { this.str = str; } fn.prototype.format = function () { var arg = ______; return this.str.replace(_______, functio

  • JavaScript中style.left与offsetLeft的使用及区别详解

    如果父div的position定义为relative,子div的position定义为absolute,那么子div的style.left的值是相对于父div的值, 这同offsetLeft是相同的,区别在于: 1. style.left 返回的是字符串,如28px,offsetLeft返回的是数值28,如果需要对取得的值进行计算,还用offsetLeft比较方便. 2. style.left是读写的,offsetLeft是只读的,所以要改变div的位置,只能修改style.left. 3. s

  • JavaScript中this的四个绑定规则总结

    前言 如果要问javascript中哪两个知识点容易混淆,作用域查询和this机制绝对名列前茅.所以这篇文章开始将介绍javascript中this的四个绑定规则,下面来一起看看吧. 绑定规则 1. 默认绑定 独立函数调用时,this 指向全局对象,如果使用严格模式,那么全局对象无法使用默认绑定, this绑定至 undefined. function foo() { console.log(this.a); } var a = 2; foo(); // 2 严格模式时: function fo

  • JavaScript中浅讲ajax图文详解

    1.ajax入门案例 1.1 搭建Web环境 ajax对于各位来说,应该都不陌生,正因为ajax的产生,导致前台页面和服务器之间的数据传输变得非常容易,同时还可以实现页面的局部刷新.通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新. 对于JavaWeb项目而言,ajax主要用于浏览器和服务器之间数据的传输. 如果是单单地堆砌知识点,会显得比较无聊,那么根据惯例,我先不继续介绍ajax,而是来写一个案例吧. 打开

  • 探寻JavaScript中this指针指向

    探寻JavaScript中this指针指向 首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然网上大部分的文章都是这样说的,虽然在很多情况下那样去理解不会出什么问题,但是实际上那样理解是不准确的,所以在你理解this的时候会有种琢磨不透的感觉),那么接下来我会深入的探讨这个问题. 为什么要学习this?如果你学过函数式编程,面向对象编程,那你肯定知

  • 浅谈JavaScript中的string拥有方法的原因

    引子 我们都知道,JavaScript数据类型分两大类,基本类型(或者称原始类型)和引用类型. 基本类型的值是保存在栈内存中的简单数据段,它们是按值访问的.JS中有五种基本类型:Undefined.Null.Boolean.Number和String. 引用类型的值是保存在堆内存中的对象,它的值是按引用访问的.引用类型主要有Object.Array.Function.RegExp.Date. 对象是拥有属性和方法的,所以我们看到下面这段代码一点也不奇怪. var favs=['鸡蛋','莲蓬']

  • javascript中强制执行toString()具体实现

    原文:Enforcing toString()译文:javascript 中强制执行 toString() 译者:singleseeker Javascript通常会根据方法或运算符的需要而自动把值转成所需的类型,这可能导致各种错误. Brian McKenna (@puffnfresh) suggests 提供了下列测试代码: 复制代码 代码如下: Object.prototype.valueOf = function () { throw new Error('Use an explicit

  • javascript中的replace函数(带注释demo)

    javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此机会,好好巩固一下相关知识点. 1.初识replace 在js中有两个replace函数 一个是location.replace(url) 跳转到一个新的url 一个string.replace("xx","yy") 替换字符串 返回一个新的字符串,该方法并不改变字符串

随机推荐