JS-24 async异步函数、 await关键字;异步函数的执行流程;进程和线程;浏览器的事件循环;宏任务和微任务;Promise面试题

news2024/11/16 2:41:17

目录

  • 1_异步函数
    • 1.1_async
    • 1.2_异步函数的执行流程
    • 2_await关键字
  • 2_浏览器进程、线程
    • 2.1_进程和线程
    • 2.2_操作系统的工作方式
    • 2.3_浏览器中的JavaScript线程
    • 2.4_浏览器的事件循环
    • 2.3_宏任务和微任务
    • 2.4_Promise面试题
      • 题一
      • 题二

1_异步函数

1.1_async

async是asynchronous单词的缩写,异步、非同步;
sync是synchronous单词的缩写,同步、同时;

async关键字用于声明一个异步函数, async异步函数可以有很多中写法

    // 异步函数
    // 写法一
    async function foo() {
      console.log("foo function1")
    }
    // 写法二
    const bar = async function() {}
    // 写法三
    const baz = async () => {}
    // 写法四
    class Person {
      async running() {}
    }

1.2_异步函数的执行流程

异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。

    // 返回值的区别
    // 1.普通函数
    function foo1() {
      return 123
    }
    console.log(foo1()) //123

异步函数有返回值时,和普通函数会有区别:

  • 情况一:异步函数的返回值相当于被包裹到Promise.resolve中;
  • 情况二:异步函数的返回值是Promise,状态由由Promise决定;
  • 情况三:异步函数的返回值是一个对象并实现了thenable,那么由对象的then方法来决定;
    // 2.异步函数
    async function foo2() {
      // 1.返回一个普通的值。被包裹到Promise.resolve
      // -> Promise.resolve(321)
      return ["abc", "cba", "nba"] //res: (3) ['abc', 'cba', 'nba']

      // 2.返回一个Promise。状态由由Promise决定
      // return new Promise((resolve, reject) => {
      //   setTimeout(() => {
      //     resolve("aaa")  //res: aaa
      //   }, 3000)
      // })

      // 3.返回一个thenable对象。由对象的then方法来决定
    //   return {
    //     then: function(resolve, reject) {
    //       resolve("bbb")  //res: bbb
    //     }
    //   }
    }

    foo2().then(res => {
      console.log("res:", res)  //res: (3) ['abc', 'cba', 'nba']
    })

如果在async中抛出了异常,程序不会像普通函数一样报错,而是会作为Promise的reject来传递;
    // 如果异步函数中有抛出异常(产生了错误), 这个异常不会被立即浏览器处理
    // 进行如下处理: Promise.reject(error)
    async function foo() {
      console.log("---------1")
      console.log("---------2")
      // "abc".filter()
      throw new Error("coderhhh async function error")
      console.log("---------3")
      return 123
    }

    // promise -> pending -> fulfilled/rejected
    foo().then(res => {
      console.log("res:", res)
    }).catch(err => {
      console.log("coderhhh err:", err)
      console.log("继续执行其他的逻辑代码")
    })

在这里插入图片描述


2_await关键字

async函数特殊之处是可以在它内部使用await关键字,而普通函数中是不可以的。

await关键字的特点?

  • await后面通常有一个表达式,这个表达式会返回一个Promise;
  • await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数;

如果await后面是一个普通的值,会直接返回这个值;
如果await后面是一个thenable的对象,会根据对象的then方法调用来决定后续的值;
如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值;

    // await条件: 必须在异步函数中使用
    function bar() {
      console.log("bar function")
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(123)
        }, 2000)  //等待2秒钟
      })
    }
    async function foo() {
      console.log("-------")
      // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
      const res1 = await bar()
      console.log("await后面的代码:", res1) //await后面的代码: 123【等待2秒后,才打印】
      const res2 = await bar()
      console.log("await后面的代码:", res2)// await后面的代码: 123  【等待2秒后,才打印
      console.log("+++++++")//+++++++
    }
    foo()

await处理异步请求

    function requestData(url) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(url)
          // reject("error message")  //如果返回的Promise状态是reject,浏览器的控制台会抛出异常信息
        }, 2000);
      })
    }

    async function getData() {
      const res1 = await requestData("hhh")   //返回的Promise状态是resolve,则继续往下执行代码
      console.log("res1:", res1)

      const res2 = await requestData(res1 + "kobe")
      console.log("res2:", res2)
    }

    getData().catch(err => {  //如果返回的Promise状态是reject,浏览器的控制台会抛出异常信息
      console.log("err:", err) //err: error message
    })

2_浏览器进程、线程

2.1_进程和线程

线程和进程是操作系统中的两个概念:进程>>线程

  • 进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式;
  • 线程(thread):操作系统能够运行运算调度的最小单位,通常情况下它被包含在进程中;

听起来很抽象,解释:

  • 进程:可以认为,启动一个应用程序,就会默认启动一个进程(也可能是多个进程);
  • 线程:每一个进程中,都会启动至少一个线程用来执行程序中的代码,这个线程被称之为主线程;
  • 所以也可以说进程是线程的容器;

一个形象的例子解释:

  • 操作系统类似于一个大工厂;
  • 工厂中里有很多车间,这个车间就是进程;
  • 每个车间可能有一个以上的工人在工厂,这个工人就是线程;

2.2_操作系统的工作方式

如何同时让多个进程(边听歌、边写代码、边查阅资料)同时工作?

  • CPU的运算速度非常快,它可以快速的在多个进程之间迅速的切换;
  • 当进程中的线程获取到时间片时,就可以快速执行编写的代码;
  • 对于用户来说是感受不到这种快速的切换的;

可以在Mac的活动监视器或者Windows的资源管理器中查看到很多进程:


2.3_浏览器中的JavaScript线程

JavaScript是单线程的,但是JavaScript的线程应该有自己的进程:浏览器或者Node。

浏览器是一个进程吗,它里面只有一个线程吗?

  • 目前多数的浏览器都是多进程的,当打开一个新页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,导致整个浏览器需要强制退出;
  • 每个进程中有很多的线程,其中包括执行JavaScript代码的线程;

JavaScript的代码执行是在一个单独的线程中执行的:

  • 这就意味着JavaScript的代码,在同一个时刻只能做一件事;
  • 如果这件事是非常耗时的,就意味着当前的线程就会被阻塞;

所以真正耗时的操作,实际上并不是由JavaScript线程在执行的:

  • 浏览器的每个进程是多线程的,那么其他线程可以来完成这个耗时的操作;
  • 比如网络请求、定时器,只需要在特性的时候执行应该有的回调即可;

2.4_浏览器的事件循环

在执行JavaScript代码的过程中,有异步操作?

  • 中间插入了一个setTimeout的函数调用;
  • 这个函数被放到入调用栈中,执行会立即结束,并不会阻塞后续代码的执行;

在这里插入图片描述


2.3_宏任务和微任务

事件循环中并非只维护着一个队列,事实上是有两个队列:

  • 宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
  • 微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()等

事件循环对于两个队列的优先级?

  • main script中的代码优先执行(编写的顶层script代码);
  • 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
    • 也就是宏任务执行之前,必须保证微任务队列是空的;
    • 如果不为空,那么就优先执行微任务队列中的任务(回调);

2.4_Promise面试题

给出下列代码的正确执行顺序(也就是打印顺序),先自己理解了,再看代码运行结果。

题一

    console.log("script start")

    setTimeout(function () {
      console.log("setTimeout1");
      new Promise(function (resolve) {
        resolve();
      }).then(function () {
        new Promise(function (resolve) {
          resolve();
        }).then(function () {
          console.log("then4");
        });
        console.log("then2");
      });
    });

    new Promise(function (resolve) {
      console.log("promise1");
      resolve();
    }).then(function () {
      console.log("then1");
    });

    setTimeout(function () {
      console.log("setTimeout2");
    });

    console.log(2);

    queueMicrotask(() => {
      console.log("queueMicrotask1")
    });

    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then3");
    });

    console.log("script end")

在这里插入图片描述


【详细解题】找出对应main javascrip、微任务、宏任务的代码。

[[main javascrip]](由上到下的代码排列)
            console.log("script start")new Promise(function (resolve) {
                  console.log("promise1");
                  resolve();
            })
            console.log(2);
            new Promise(function (resolve) {
                  resolve();
            })
            console.log("script end")

[[微任务]]
Promise.then()
queueMicrotask()

[[宏任务]]
setTimeout()  所以该函数内的promise启动需等待该函数执行后


【详细解题】【具体的逻辑】
(1)先把main javascrip代码执行完毕,即打印出的前四行。
            script start
            promise1
            2
            script end
(2)执行微任务的代码。
除去已经打印的main javascrip,看到setTimeout() 函数先跳过;由代码的上下熟悉怒,于是进入这个微任务内部。
            new Promise(function (resolve) {
              console.log("promise1");
              resolve();
            }).then(function () {
              console.log("then1");
            });
     老样子,根据main javascrip、微任务、宏任务的顺序。由于console.log("promise1");已经执行过,继续往下。即来到.then()方法内,于是执行console.log("then1"),打印then1。
     继续往下,来到下面这个微任务内部。由于只有内部简单,执行 console.log("queueMicrotask1"),打印queueMicrotask1。
                    queueMicrotask(() => {
                      console.log("queueMicrotask1")
                    });

	继续往下,来到下面这个微任务内部。main JavaScript没有打印,故往下进入.then()内部,执行console.log("then3"),打印then3。
                new Promise(function (resolve) {
                  resolve();
                }).then(function () {
                  console.log("then3");
                });
至此,当前微任务执行完毕,可进行宏任务的执行。

(3)来到宏任务。
按照上下顺序,来到这个宏任务内部。
                setTimeout(function () {
                  console.log("setTimeout1");
                  new Promise(function (resolve) {
                    resolve();
                  }).then(function () {
                    new Promise(function (resolve) {
                      resolve();
                    }).then(function () {
                      console.log("then4");
                    });
                    console.log("then2");
                  });
                });
先找到main JavaScript的打印代码并执行。可见,在该宏任务内部,出来含有.then()方法的,都属于微任务,跳过。即可打印setTimeout1和then2。轮到微任务执行。进入.then()方法,打印then4。该函数执行完毕。

往下走,来到下面的宏任务。内部不复杂,直接打印setTimeout2。
    setTimeout(function () {
      console.log("setTimeout2");
    });

题二

加入了异步函数,考察对await的理解

    async function async1 () {
      console.log('async1 start')
      await async2();
      console.log('async1 end')
    }

    async function async2 () {
      console.log('async2')
    }

    console.log('script start')

    setTimeout(function () {
      console.log('setTimeout')
    }, 0)
    
    async1();
    
    new Promise (function (resolve) {
      console.log('promise1')
      resolve();
    }).then (function () {
      console.log('promise2')
    })

    console.log('script end')

在这里插入图片描述


【详细解题】找出对应main javascrip、微任务、宏任务的代码。

[[main javascrip]](由上到下的代码排列)
            console.log('script start')
            async1();
console.log('script end')

[[微任务]]
async2 ()

[[宏任务]]
setTimeout()  虽然该函数的时间设置为0,但是根据定义,是宏任务,就必须等前面两项执行完毕。

【详细解题】【具体的逻辑】
(1)先把main javascrip代码执行。
前面两个函数定义,但是未执行,故打印script start。
遇到setTimeout()函数,直接归类到宏任务队列。
进入async1()函数,打印 async1 start。
async1()函数由于遇到await,先执行async2()函数,故打印async2。
但需要将其后代码console.log('async1 end')加入到微任务队列。
至此,async1()函数执行完毕
遇到new Promise()函数,进入执行console.log('promise1'),故打印promise1。
遇到new Promise()函数的.then()函数,添加到微任务队列。
至此,new Promise()函数执行完毕。
遇到console.log('script end'),打印script end。

(2)执行微任务的代码。
console.log('async1 end')
new Promise()函数的.then()函数
微任务队列,按照先来后到顺序,先打印async1 end,后打印promise2。
至此,微任务执行完毕。

(3)执行宏任务。
宏任务只有一个 setTimeout()函数,不复杂,直接打印setTimeout。
    setTimeout(function () {
      console.log('setTimeout')
    }, 0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/757592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型基础:理论与技术的演进概述

大模型基础:理论与技术的演进概述 人工智能发展历程 人工智能发展历程可以概括为以下几个主要阶段: 起源阶段(1956-1980年代),这一时期被称为人工智能的“黄金时代”, 达特茅斯会议首次提出人工智能概念, 开发出传统人工智能系统, 如ELIZA、深蓝等。知…

【InsCode Stable Diffusion 美图活动一期】生成着玩

此为内容创作模板,请按照格式补充内容,在发布之前请将不必要的内容删除 一、 Stable Diffusion 模型在线使用地址: https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置: 三、图片生成提示词与反向…

C++ | 多态

目录 前言 一、多态的概念 二、多态的定义与使用 1、多态的构成条件 2、虚函数 3、虚函数的重写(覆盖) 4、虚函数重写的两个例外 (1)协变 (2)析构函数的重写 5、子类的指针或者引用调用 6、C11的…

unity对象池系统

当游戏场景中出现大量的可重复利用的物体时,通过Destory来销毁再创建会触发不必要的GC回收机制,浪费性能,我们可以利用unity自带的对象池系统,从而节约性能来得到同样的效果。 为了使用这个对象池系统,我写了一个瞬间产…

element-plus 报错 ResizeObserver loop limit exceeded 解决

使用Element-plus,页面重置大小时,出现如下报错: Uncaught runtime errors: ERROR ResizeObserver loop limit exceeded at handleError (webpack-internal:///./node_modules/webpack-dev-server/client/overlay.js:252:58) at ev…

DETR3D: 3D Object Detection from Multi-view Images via 3D-to-2D Queries

DETR3D: 3D Object Detection from Multi-view Images via 3D-to-2D Queries 目的 本文提出了一个 仅使用 2D 信息的,3D 目标检测网络,并且比依赖与 密集的深度预测 或者 3D 重建的过程。该网络使用了 和 DETR 相似的 trasformer decoder ,…

SpringBoot中集成jasypt-spring-boot实现配置文件数据加密脱敏

场景 经常会遇到这样一种情况:项目的配置文件中总有一些敏感信息,比如数据源的url、用户名、 密码....这些信息一旦被暴露那么整个数据库都将会被泄漏,那么如何将这些配置隐藏呢。 除了使用手动将加密之后的配置写入到配置文件中&#xff…

Win把老外惹恼了!

随着Windows 10创意者更新(民间称Win10.3)推送规模的加大,其暴露出来的槽点越来越多。 此前,我们已经报道过新版的Defender升级为安全助手,类似于国内有名的xx管家和数字卫士,全面接管PC健康事务。 然而&a…

【css】用css样式快速写右上角badge徽标,颜色设置为渐变色

先看效果展示&#xff0c;已公开显示在图片卡片的右上角。 首先是dom代码&#xff1a;需要两个view或者div&#xff0c;public-badge是“已公开”那个矩形&#xff0c;show-signal是右边那个下三角&#xff0c;也就是阴影部分&#xff0c;这样看起来比较有立体感。 <view…

Linux驱动进阶(四)——内外存访问

文章目录 前言内存分配kmalloc函数vmalloc()函数后备高速缓存 页面分配内存分配物理地址和虚拟地址之间的转换 设备I/O端口的访问Linux I/O端口读写函数I/O内存读写使用I/O端口 小结 前言 驱动程序加载成功的一个关键因素&#xff0c;就是内核能够为驱动程序分配足够的内存空间…

论文解读:Inpaint Anything: Segment Anything Meets Image Inpainting

论文&#xff1a;https://arxiv.org/pdf/2304.06790.pdf 代码&#xff1a;https://github.com/geekyutao/Inpaint-Anything 图1&#xff1a;Inpaint Anything示意图。用户可以通过点击图像中的任何对象来选择它。借助强大的视觉模型&#xff0c;例如SAM[7]、LaMa [13]和稳定扩散…

我叫李明,我是一名开发人员

目录 一、这是一个故事 二、不屈不挠的李明 三、化解于无形 四、总结 一、这是一个故事 这个故事的主人公是一个年轻的程序员&#xff0c;他叫做李明。李明是一名技术过硬、工作认真负责的程序员&#xff0c;他的工作是开发一款新的软件产品。这款软件是一款在线购物平…

【Java基础】第四章 Object 类应用

系列文章目录 [Java基础] 第一章 String类应用及分析 [Java基础] 第二章 数组应用及源码分析 [Java基础] 第三章 StringBuffer 和 StringBuilder 类应用及源码分析 [Java基础] 第四章 Object 类应用 文章目录 系列文章目录前言一、如何使用Object&#xff1f;1.1、显式继承1.2…

c++内存映射文件

概念 将一个文件直接映射到进程的进程空间中&#xff08;“映射”就是建立一种对应关系,这里指硬盘上文件的位置与进程逻辑地址空间中一块相同区域之间一 一对应,这种关系纯属是逻辑上的概念&#xff0c;物理上是不存在的&#xff09;&#xff0c;这样可以通过内存指针用读写内…

Web-登录功能实现(含JWT令牌)

登录功能 这个登陆功能先不返回JWT令牌 登陆会返回JWT令牌 一会在登陆验证时讲解JWT令牌&#xff08;返回的data就是它&#xff09; 登录校验 概述 就是你比如复制一个url 用一个未曾登陆对应url系统的浏览器访问 他会先进入登陆页面 登陆校验就是实现这个功能 简而言之…

基于EasyExcel的单元格合并自定义算法处理

基于EasyExcel导出Excel后&#xff0c;通过对合并单元格的简单规则配置&#xff0c;实现如下图所示的单元格合并效果&#xff1a; 效果截图 原表格数据如下&#xff1a; 通过配置单元格合并规则后&#xff0c;生成的合并后的表格如下&#xff1a; 注&#xff1a;其中第三列&a…

Android Studio连接安卓手机

1. 创建项目 2. 下载Google USB Driver 点击右上角红框的【SDK Manager】->【SDK Tools】。 也可以在 【tools】->【SDK Manager】->【SDK Tools】下进入。 点击Google USB Driver&#xff0c;下载后点ok。 3. 环境变量 右键【我的电脑】->【高级系统设置】-&g…

基于微信小程序的高校新生自助报道系统设计与实现(Java+spring boot+MySQL+小程序)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于微信小程序的高校新生自助报道系统设计与实现&#xff08;Javaspring bootMySQL微信小程序&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1…

123、仿真-基于51单片机的电流控制仿真系统设计(Proteus仿真+程序+原理图+参考论文+配套资料等)

方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ&#xff0c;在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节…

java报错- 类文件具有错误的版本 61.0, 应为 52.0 请删除该文件或确保该文件位于正确的类路径子目录中。

SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17&#xff0c;所以仅需将SpringBoot版本降低为3.0以下即可。