重新认识async、await

以下是一些事件循环机制例子

0.两个例子先测下实力

async function async1 () {await new Promise((resolve, reject) => { // 右边是Promise无需等待resolve()})console.log('A')
}async1()new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})
// 最终结果👉: B A C D

解析:调用async1,有await等待一个promise没有返回值,跳出,console.log(‘A’)不执行。打印B,有.then把C放入微任务。现在执行await后面的打印A。之后在取出微任务的打印C,最后是D。结果就是B A C D

async function async1 () {await async2()console.log('A')
}async function async2 () {return new Promise((resolve, reject) => { // 从async视角等待2个thenresolve()})
}async1()new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})// 最终结果👉: B C D A

解析:执行async1, async1中await了async2, async2返回了Promise但是也是没有明确的值,跳出,console.log(‘A’)不执行。来到下面打印B,之后微任务放入C,(解释一下这个为什么没有打印A,跟上面哪里不一样?)取出微任务中的,打印C,之后打印D,最后打印A。⚠️这里与上面输出不同是因为,第二个await后面跟的是一个async函数,是async函数就要根据它返回值的类型进行不同数目的微任务第二个例子中async2返回了一个Promise,根据结论,返回值为一个Promise的需要等待 2个then的时间,那么等待then©、then(D)两个后才会打印A。

1.async 函数返回值

在讨论 await 之前,先聊一下async 函数处理返回值的问题,它会像 Promise.prototype.then 一样,会对返回值的类型进行辨识。

👉根据返回值的类型,引起 js引擎 对返回值处理方式的不同

📑结论:async函数在抛出返回值时,会根据返回值类型开启不同数目的微任务
return结果值:非thenable、非promise(不等待)
return结果值:thenable(等待 1个then的时间)
return结果值:promise(等待 2个then的时间)

例1:

async function testA () {return 1; // 非thenable、非promise
}testA().then(() => console.log(1));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));// (不等待)最终结果👉: 1 2 3

例子2:

async function testB () {return {then (cb) { // thenablecb();}};
}testB().then(() => console.log(1));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));// (等待一个then)最终结果👉: 2 1 3

例子3:

async function testC () {return new Promise((resolve, reject) => { // promiseresolve()})
}testC().then(() => console.log(1));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));// (等待两个then)最终结果👉: 2 3 1async function testC () {return new Promise((resolve, reject) => { // promiseresolve()})
} testC().then(() => console.log(1));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3)).then(() => console.log(4))// (等待两个then)最终结果👉: 2 3 1 4

看了这三个🌰是不是对上面的结论有了更深的认识
稍安勿躁,来试试一个经典面试题👇

async function async1 () {console.log('1')await async2()console.log('AAA')
}async function async2 () {console.log('3')return new Promise((resolve, reject) => {resolve()console.log('4')})
}
​
console.log('5')setTimeout(() => {console.log('6')
}, 0);async1()new Promise((resolve) => {console.log('7')resolve()
}).then(() => {console.log('8')
}).then(() => {console.log('9')
}).then(() => {console.log('10')
})
console.log('11')// 最终结果👉: 5 1 3 4 7 11 8 9 AAA 10 6

如果根据上面的结论来,这题应该没问题。
5 1 3 4 ,这里的promise需要等待2个then的时间。7也没问题。遇见.then8放进微任务, 打印11。从微任务队列取出打印8,9放进微任务,取出打印9,10放进微任务。至此等待的2个then已经完成。回到async1中将await async2()后面的打印AAA。取出打印10,最后是宏任务setTimeout中的6。

步骤拆分👇:
0.先执行同步代码,输出5
1.执行setTimeout,是放入宏任务异步队列中
2.接着执行async1函数,输出1
3.执行async2函数,输出3
4.Promise构造器中代码属于同步代码,输出4

async2函数的返回值是Promise,等待2个then后放行,所以AAA暂时无法输出

5.async1函数暂时结束,继续往下走,输出7
6.同步代码,输出11
7.执行第一个then,输出8
8.执行第二个then,输出9
9.终于等到了两个then执行完毕,执行async1函数里面剩下的,输出AAA
10.再执行最后一个微任务then,输出10
11.执行最后的宏任务setTimeout,输出6

2. await 右值类型区别

2.1、非 thenable(会立即向微任务队列添加一个微任务then,但不需等待)

🌰1:

async function test () {console.log(1);await 1;console.log(2);
}test();
console.log(3);
// 最终结果👉: 1 3 2

🌰2:

function func () {console.log(2);
}async function test () {console.log(1);await func();console.log(3);
}test();
console.log(4);// 最终结果👉: 1 2 4 3

🌰3:

async function test () {console.log(1);await 123 // 非 thenable,无需等待console.log(2);
}test();
console.log(3);
​
Promise.resolve().then(() => console.log(4)).then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7));// 最终结果👉: 1 3 2 4 5 6 7

Note:
await后面接非 thenable 类型,会立即向微任务队列添加一个微任务then,但不需等待

2.2、thenable类型(等待一个then 的时间之后执行)

async function test () {console.log(1);await {then (cb) { // thenable需等待一个then时间cb();},};console.log(2);
}test();
console.log(3);
​
Promise.resolve().then(() => console.log(4)).then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7));// 最终结果👉: 1 3 4 2 5 6 7

Note:
await 后面接 thenable 类型,需要等待一个 then 的时间之后执行

2.3、Promise类型(同非thenable无需等待)

async function test () {console.log(1);await new Promise((resolve, reject) => { // 无需等待,但正常是2个then时间resolve()})console.log(2);
}test();
console.log(3);
​
Promise.resolve().then(() => console.log(4)).then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7));// 最终结果👉: 1 3 2 4 5 6 7

❓为什么表现的和非 thenable 值一样呢?为什么不等待两个 then 的时间呢?

Note:

  • TC 39(ECMAScript标准制定者) 对await 后面是 promise
    的情况如何处理进行了一次修改,移除了额外的两个微任务,在早期版本,依然会等待两个 then 的时间
  • 掘金上有大佬翻译了官方解释:更快的async 函数和 promises, 但在这次更新中并没有修改 thenable 的情况

这样做可以极大的优化 await 等待的速度👇

async function func () {console.log(1);await 1; // 非 thenable,无需等待1个thenconsole.log(2);await 2;console.log(3);await 3;console.log(4);
}async function test () {console.log(5);await func();console.log(6);
}test();
console.log(7);
​
Promise.resolve().then(() => console.log(8)).then(() => console.log(9)).then(() => console.log(10)).then(() => console.log(11));// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

前面5 1 7没问题,正常要从微任务对列中拿出8,但是await func();中await 1; 非 thenable,无需等待1个then,所以先打印出了2。之后走的打印8,放入9。本来要打印9的,但是又到了await 2; 非 thenable,无需等待1个then,所以先打印出了3。之后取出打印9。以此类推,到await func();全部执行完。打印6,最后是11。

Note:
await Promise.prototype.then 虽然很多时候可以在时间顺序上能等效,但是它们之间有本质的区别

1.test 函数中的 await 会等待 func 函数中所有的 await 取得 恢复函数执行 的命令并且整个函数执行完毕后才能获得取得 恢复函数执行的命令;
2.test 函数的 await 此时不能在时间的顺序上等效 then,而要等待到 func 函数完全执行完毕;
3.比如这里的数字6很晚才输出,如果单纯看成then的话,在下一个微任务队列执行时6就应该作为同步代码输出了才对。

所以我们可以合并两个函数的代码👇

async function test () {console.log(5);// 原来​func()中的console.log(1);await 1;console.log(2);await 2;console.log(3);await 3;console.log(4);await null;console.log(6);
}test();
console.log(7);
​
Promise.resolve().then(() => console.log(8)).then(() => console.log(9)).then(() => console.log(10)).then(() => console.log(11));// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

因为将原本的函数融合,此时的 await 可以等效为Promise.prototype.then,又完全可以等效如下代码👇

async function test () {console.log(5);console.log(1);Promise.resolve().then(() => console.log(2)).then(() => console.log(3)).then(() => console.log(4)).then(() => console.log(6))
}test();
console.log(7);
​
Promise.resolve().then(() => console.log(8)).then(() => console.log(9)).then(() => console.log(10)).then(() => console.log(11));// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

以上三种写法在时间的顺序上完全等效,所以你 完全可以将 await后面的代码可以看做在 then 里面执行的结果,又因为 async 函数会返回 promise 实例,所以还可以等效成👇

async function test () {console.log(5);console.log(1);
}test().then(() => console.log(2)).then(() => console.log(3)).then(() => console.log(4)).then(() => console.log(6))
​
console.log(7);
​
Promise.resolve().then(() => console.log(8)).then(() => console.log(9)).then(() => console.log(10)).then(() => console.log(11));// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

可以发现,test 函数全是走的同步代码…

所以👉:async/await 是用同步的方式,执行异步操作

3.例子

例1:

async function async2 () {new Promise((resolve, reject) => { // async返回值是非thenable,无需等待resolve()})
}async function async3 () {return new Promise((resolve, reject) => { // async返回值是promise需要等待2个thenresolve()})
}async function async1 () {// 方式一:最终结果:B A C D// await new Promise((resolve, reject) => { // 同非thenable无需等待,最新规定已经移除了等待2个then//     resolve()// })// 方式二:最终结果:B A C D// await async2()// 方式三:最终结果:B C D Aawait async3()
​console.log('A')
}async1()new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})

大致思路👇:
首先,async函数的整体返回值永远都是Promise,无论值本身是什么

  • 方式一:await的是Promise,无需等待
  • 方式二:await的是async函数,但是该函数的返回值本身是非thenable,无需等待
  • 方式三:await的是async函数,且返回值本身是Promise,需等待两个then时间

例2: 又搞不懂了?

function func () {console.log(2);// 方式一:1 2 4  5 3 6 7// Promise.resolve() //拿到确切的func函数返回值undefined//     .then(() => console.log(5))//     .then(() => console.log(6))//     .then(() => console.log(7))// 方式二:1 2 4  5 6 7 3return Promise.resolve() // 拿到func函数返回值,但是并未获得具体的结果.then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7))
}async function test () {console.log(1);await func(); // 方式一 拿到确切值undefined等待了一个thenconsole.log(3);
}test();
console.log(4); 

步骤拆分👇:
方式一:
同步代码输出12,接着将log(5)处的then1加入微任务队列,await拿到确切的func函数返回值undefined

将后续代码console.log(3);放入微任务队列(then2,可以这样理解)

执行同步代码输出4,到此,所有同步代码完毕
执行第一个放入的微任务then1输出5,产生log(6)的微任务then3

执行第二个放入的微任务then2输出3

然后执行微任务then3,输出6,产生log(7)的微任务then4
执行then4,输出7

方式二:
同步代码输出12await拿到func函数返回值,但是并未获得具体的结果(由Promise本身机制决定),暂停执行当前async函数内的代码(跳出、让行)
输出4,到此,所有同步代码完毕
await一直等到Promise.resolve().then...执行完成,再放行输出3

方式二没太明白❓

function func () {console.log(2);return Promise.resolve().then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7))
}async function test () {console.log(1);await func() // 没拿到确切值,await一直等到Promise.resolve().then...执行完成console.log(3);
}test();
console.log(4);new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})// 最终结果👉: 1 2 4    B 5 C 6 D 7 3

还是没懂?

继续👇

async function test () {console.log(1);await Promise.resolve() // 怎么算有确切值呢?.then(() => console.log(5)).then(() => console.log(6)).then(() => console.log(7))console.log(3);
}test();
console.log(4);new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})// 最终结果👉: 1 4    B 5 C 6 D 7 3

这里也是懵
Note:
综上,await一定要等到右侧的表达式有确切的值才会放行,否则将一直等待(阻塞当前async函数内的后续代码),不服看看这个👇

function func () {return new Promise((resolve) => {console.log('B')// resolve() 故意一直保持pending})
}async function test () {console.log(1);await func()console.log(3);
}test();
console.log(4);
// 最终结果👉: 1 B 4 (永远不会打印3)
​
​
// ---------------------或者写为👇-------------------
async function test () {console.log(1);await new Promise((resolve) => {console.log('B')// resolve() 故意一直保持pending})console.log(3);
}test();
console.log(4);
// 最终结果👉: 1 B 4 (永远不会打印3)

例3:

async function func () {console.log(2);return { // 等待一个thenthen (cb) { cb()}}
}async function test () {console.log(1);await func();console.log(3);
}test();
console.log(4);new Promise((resolve) => {console.log('B')resolve()
}).then(() => {console.log('C')
}).then(() => {console.log('D')
})// 最终结果👉: 1 2 4 B C 3 D

步骤拆分👇:
同步代码输出1、2
await拿到func函数的具体返回值thenable,将当前async函数内的后续代码放入微任务then1(但是需要等待一个then时间)
同步代码输出4、B,产生log(C)的微任务then2
由于then1滞后一个then时间,直接执行then2输出C,产生log(D)的微任务then3
执行原本滞后一个then时间的微任务then1,输出3
执行最后一个微任务then3输出D

其他例子:
setTimeout+Promise+Async输出顺序?很简单呀!

4.总结

4.1 async函数返回值

📑结论:async函数在抛出返回值时,会根据返回值类型开启不同数目的微任务
return结果值:非thenable、非promise(不等待)
return结果值:thenable(等待 1个then的时间)
return结果值:promise(等待 2个then的时间)

4.2 await右值类型区别

  • 接非 thenable 类型,会立即向微任务队列添加一个微任务then,但不需等待
  • thenable 类型,需要等待一个 then 的时间之后执行
  • Promise类型(有确定的返回值),会立即向微任务队列添加一个微任务then,但不需等待
    • TC 39 对await 后面是 promise 的情况如何处理进行了一次修改,移除了额外的两个微任务,在早期版本,依然会等待两个 then 的时间

作者:Squirrel_
链接:https://juejin.cn/post/7194744938276323384
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

04-转录组下游分析-标准化、聚类、差异分析

准备工作 1.数据标准化 标准化前需要进行数据预处理 过滤低表达的基因,并检查是否有异常样本 以下是常见的几种过滤方式(过滤的标准都可以自己调整) 1:在至少在75%的样本中都表达的基因(表达是指在某个样本中count值…

常见网络厂商设备默认用户名/密码大全

常见网络厂商的默认用户名/密码 01 思科 (Cisco) 设备类型:路由器、交换机、防火墙、无线控制器 默认用户名:cisco 默认密码:cisco 设备类型:网管型交换机 默认用户名:admin 默认密码:admin 02 华…

Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系

一.什么是Spring?它解决了什么问题? 1.1什么是Spring? Spring,一般指代的是Spring Framework 它是一个开源的应用程序框架,提供了一个简易的开发方式,通过这种开发方式,将避免那些可能致使代码…

【热门主题】000055 网络安全:构筑数字时代的坚固防线

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【热…

鸿蒙中位置权限和相机权限

1.module.json5中添加相关权限和string.json中配置信息 2. import { hilog } from kit.PerformanceAnalysisKit; import { TAG } from ohos/hypium/src/main/Constant; import { bundleManager, common } from kit.AbilityKit; import { abilityAccessCtrl } from kit.Ability…

2024.6使用 UMLS 集成的基于 CNN 的文本索引增强医学图像检索

Enhancing Medical Image Retrieval with UMLS-Integrated CNN-Based Text Indexing 问题 医疗图像检索中,图像与相关文本的一致性问题,如患者有病症但影像可能无明显异常,影响图像检索系统准确性。传统的基于文本的医学图像检索&#xff0…

H.264/H.265播放器EasyPlayer.js网页直播/点播播放器关于播放的时候就有声音

EasyPlayer.js H5播放器,是一款能够同时支持HTTP、HTTP-FLV、HLS(m3u8)、WS、WEBRTC、FMP4视频直播与视频点播等多种协议,支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式,支持MSE、WASM、WebCodec等多种解码方…

Redis 的代理类注入失败,连不上 redis

在测试 redis 是否成功连接时&#xff0c;发现 bean 没有被创建成功&#xff0c;导致报错 根据报错提示&#xff0c;需要我们添加依赖&#xff1a; <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>&l…

Prometheus结合K8s(一)搭建

公司之前K8s集群没有监控&#xff0c;top查看机器cpu使用率很高&#xff0c;为了监控pod的cpu和内存&#xff0c;集群外的mysql资源&#xff0c;初步搭建了Prometheus监控系统 提前准备镜像 docker.io/grafana/grafana 10.4.4 docker.io/prom/prometheus v2.47.2 docker.io/…

Vscode/Code-server无网环境安装通义灵码

Date: 2024-11-18 参考材料&#xff1a;https://help.aliyun.com/zh/lingma/user-guide/individual-edition-login-tongyi-lingma?spma2c4g.11186623.0.i0 1. 首先在vscode/code-server插件市场中安装通义插件&#xff0c;这步就不细说了。如果服务器没网&#xff0c;会问你要…

【划分型DP-约束划分个数】力扣813. 最大平均值和的分组

给定数组 nums 和一个整数 k 。我们将给定的数组 nums 分成 最多 k 个非空子数组&#xff0c;且数组内部是连续的 。 分数 由每个子数组内的平均值的总和构成。 注意我们必须使用 nums 数组中的每一个数进行分组&#xff0c;并且分数不一定需要是整数。 返回我们所能得到的最…

IDEA:2023版远程服务器debug

很简单&#xff0c;但是很多文档没有写清楚&#xff0c;wocao 一、首先新建一个远程jvm 二、配置 三、把上面的参数复制出来 -agentlib:jdwptransportdt_socket,servery,suspendn,address5005 四、然后把这串代码放到服务器中 /www/server/java/jdk1.8.0_371/bin/java -agentl…

centos安装jenkins

本机使用虚拟机centos 7.9.2009 安装gitlab&#xff0c;本机的虚拟机ip地址是 192.168.60.151&#xff0c; 步骤记录如下 1、下载jenkins&#xff0c;安装jenkins之前需要安装jdk jdk和jenkins的版本对应关系参考&#xff1a;Redhat Jenkins Packages Index of /redhat-stable…

蜀道山CTF<最高的山最长的河>出题记录

出这道题的最开始感觉就是,因为现在逆向的形式好多,我最开始学习的时候,经常因为很多工具,或者手段完全不知道,就很懵逼,很多师傅都出了各种类型的,我就想着给以前的"自己"出一道正常exe,慢慢调的题,为了不那么简单,我就选择了C(究极混淆,可能比rust好点),让大家无聊…

中伟视界:AI智能分析算法如何针对非煤矿山的特定需求,提供定制化的安全生产解决方案

非煤矿山智能化改造&#xff0c;除了政策文件&#xff0c;上级监管单位需要安装的AI智能分析算法功能之外的&#xff0c;矿方真正关心的&#xff0c;能解决矿方安全生产隐患的AI智能分析算法功能有哪些呢&#xff1f; 经过与矿方的现场交流沟通&#xff0c;收集第一现场人员对安…

如何生成python项目需要的最小requirements.txt文件?

今天咱们来聊聊 Python 项目中如何生成一个“最小的” requirements.txt 文件。我们都知道&#xff0c;当我们开发一个 Python 项目的时候&#xff0c;很多时候都会在一个虚拟环境中进行&#xff0c;这样一来&#xff0c;就能避免不同项目之间的依赖冲突。 可有时候&#xff0c…

每日论文22-24ESSERC一种54.6-65.1GHz多路径同步16振荡器

《A 54.6-65.1 GHz Multi-Path-Synchronized 16-Core Oscillator Achieving −131.4 dBc/Hz PN and 195.8 dBc/Hz FoMT at 10 MHz Offset in 65nm CMOS》24欧洲固态 本文是在60GHz 16核VCO的工作&#xff0c;主要亮点在于每一组中四个VCO之间的三路同步拓扑结构&#xff0c;有…

web——upload-labs——第十一关——黑名单验证,双写绕过

还是查看源码&#xff0c; $file_name str_ireplace($deny_ext,"", $file_name); 该语句的作用是&#xff1a;从 $file_name 中去除所有出现在 $deny_ext 数组中的元素&#xff0c;替换为空字符串&#xff08;即删除这些元素&#xff09;。str_ireplace() 在处理时…

网络安全之国际主流网络安全架构模型

目前&#xff0c;国际主流的网络安全架构模型主要有&#xff1a; ● 信息技术咨询公司Gartner的ASA&#xff08;Adaptive Security Architecture自适应安全架构&#xff09; ● 美国政府资助的非营利研究机构MITRE的ATT&CK&#xff08;Adversarial Tactics Techniques &…

StarRocks 架构

StarRocks 是什么&#xff1f;&#xff08; What is StarRocks?&#xff09; StarRocks 是 MPP 的查询引擎&#xff0c;用来做实时查询&#xff0c;提供亚秒级的查询性能。 兼容 MYSQL 协议&#xff0c;可以和大部分 BI 工具进行无缝衔接。 Apache 2.0 开源产品。 使用场景&…