背景

我们知道浏览器有一个特定的事件执行机制,专业名词叫做Event Loop。如下图所示,浏览器会优先执行同步代码,遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。

微任务和宏任务

不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。下面来看以下代码的执行顺序:

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

首先先来解释下上述代码的 async 和 await 的执行顺序。

当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise。接下来在遇到 await的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。

然后当同步代码全部执行完毕以后,就会去执行所有的异步代码,那么又会回到 await 的位置执行返回的 Promise 的 resolve 函数,这又会把 resolve 丢到微任务队列中,接下来去执行 then 中的回调,当两个 then 中的回调全部执行完毕以后,又会回到 await 的位置处理返回值,这时候你可以看成是 Promise.resolve(返回值).then(),然后 await 后的代码全部被包裹进了 then 的回调中,所以 console.log('async1 end') 会优先执行于 setTimeout。

如果你觉得上面这段解释还是有点绕,那么我把 async 的这两个函数改造成你一定能理解的代码

new Promise((resolve, reject) => {  console.log('async2 end')  // Promise.resolve() 将代码插入微任务队列尾部  // resolve 再次插入微任务队列尾部  resolve(Promise.resolve())}).then(() => {  console.log('async1 end')})

也就是说,如果 await 后面跟着 Promise 的话,async1 end 需要等待三个 tick 才能执行到。那么其实这个性能相对来说还是略慢的,所以 V8 团队借鉴了 Node 8 中的一个 Bug,在引擎底层将三次 tick 减少到了二次 tick。但是这种做法其实是违法了规范的,当然规范也是可以更改的,这是 V8 团队的一个 PR,目前已被同意这种做法。

所以 Event Loop 执行顺序如下所示:

  • 首先执行同步代码,这属于宏任务
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后,如有必要会渲染页面
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数

所以以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。

微任务包括 process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。

宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。

注意:这里很多人会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。

async js 返回值_图文讲解浏览器执行JS过程中的微任务和宏任务相关推荐

  1. async js 返回值_获取JavaScript异步函数的返回值

    今天研究一个小问题: 怎么拿到JavaScript异步函数的返回值? 1.错误尝试 当年未入行时,我的最初尝试: function getSomething() { var r = 0; setTim ...

  2. controller调用另一个controller中的方法 获取返回值_必须掌握!你知道 Spring 中运用的 9 种设计模式吗 ?...

    Spring中涉及的设计模式总结,在面试中也会经常问道 Spring 中设计模式的问题.本文以实现方式.实质.实现原理的结构简单介绍 Sping 中应用的 9 种设计模型,具体详细的刨析会在后面的文章 ...

  3. cmd执行命令不等待返回值_[CVE20199535] Iterm2命令执行的不完整复现

    CVE-2019-9535 昨天爆出了一个Iterm2的代码执行漏洞,看着非常的刺激吓人,因为我也在用,所以趁热赶紧尝试复现一下.源头文章是来自:https://blog.mozilla.org/se ...

  4. async js 返回值_JS异步编程 | Async / Await / Generator 实现原理解析

    async/await实现 在多个回调依赖的场景中,尽管Promise通过链式调用取代了回调嵌套,但过多的链式调用可读性仍然不佳,流程控制也不方便,ES7 提出的async 函数,终于让 JS 对于异 ...

  5. mybatis delete返回值_面试:谈谈你对MyBatis执行过程之SQL执行过程理解

    前言 在了解了MyBatis初始化加载过程后,我们也应该研究看看SQL执行过程是怎样执行?这样我们对于Mybatis的整个执行流程都熟悉了,在开发遇到问题也可以很快定位到问题. 更重要的,在面试中遇到 ...

  6. mybatis mysql 调用存储过程 多个返回值_图解MyBatis的SQL执行流程(干货)

    前言 MyBatis可能很多人都一直在用,但是MyBatis的SQL执行流程可能并不是所有人都清楚了,那么既然进来了,通读本文你将收获如下: 1.Mapper接口和映射文件是如何进行绑定的 2.MyB ...

  7. 爬虫之selenium控制浏览器执行js代码

    爬虫之selenium控制浏览器执行js代码 selenium可以让浏览器执行我们规定的js代码,运行下列代码查看运行效果 import time from selenium import webdr ...

  8. 从输入 URL 到浏览器接收的过程中发生了什么事情

    从输入 URL 到浏览器接收的过程中发生了什么事情? 原文:http://www.codeceo.com/article/url-cpu-broswer.html 从触屏到 CPU  首先是「输入 U ...

  9. async js 返回值_async函数的返回值

    async函数其实是Geneator函数的语法糖. 1.async函数的返回值是Promise对象,可以用then方法指定下一步的操作.async函数可以看做多个异步操作,包装成一个Promise对象 ...

最新文章

  1. Kernel数据结构移植(list和rbtree)
  2. 01-迭代开发的基本需求和Scrum标准
  3. 搞懂function(*args,**kwargs)
  4. 在java中使用SPI创建可扩展的应用程序
  5. 【OpenCV 例程200篇】39. 图像灰度的线性变换
  6. 演练 类的定义 java 1615134691
  7. C语言:学生信息管理程序
  8. 一加手机安装鸿蒙系统,【新机】华为MatePad Pro 2官宣,刘作虎点赞鸿蒙手机
  9. MyBatis的ResultMaps之一对多关系
  10. nginx 失败问题集合
  11. xp的服务器系统怎么安装系统安装,XP系统怎么安装SQL Server 2000数据库
  12. 浙大计算机专业硕士专业代码,浙江大学海洋学院电子信息(专硕)专业代码
  13. macOS: 卸载 Flash Player 迎接 2021
  14. oracle 删除索引报错ORA-01418:sepecified index does not exist
  15. R统计绘图-使用rgl或pca3D包绘制3DPCA图
  16. java中Graphics类的使用
  17. NDB Cluster基本操作
  18. 形式语言与自动机 第4章 正规文法和正规集的性质
  19. PE文件之IMAGE_DOS_HEADER
  20. 20210622——Redis概述(一)

热门文章

  1. 如何利用永洪自服务数据集,构建强大的数据处理能力?
  2. 用pkg给手机装linux,pkg-config的使用方法
  3. scenebuilder各控件属性介绍_C#控件及常用设计整理(三)
  4. idea findbugs使用_「测试」 - 静态测试 amp; FindBugs
  5. android 状态栏 背景色_技术一面:说说Android动态换肤实现原理
  6. 详细讲解python中的析构方法;
  7. python约束 与MD5加密写法
  8. Python教程:对 a = [lambda : x for x in range(3)] 的理解
  9. Python-自定义函数-参数
  10. python 查询文本文件的层次