理解异步函数async和await的用法

article/2025/9/14 9:34:48

定义

1. async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行
2. async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;
3. async/await的作用就是使异步操作以同步的方式去执行

一. 关于async

async的用法,语法很简单,在函数前面加上async关键字,表示函数是异步的。

 async function timeout() {return 'hello world!'}

只有一个作用,他的调用会返回一个promise对象。
那怎么调用呢?async 函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async 函数调用之后加一句console.log;

 async function timeout() {return 'hello world!'}timeout()console.log('我虽然在后面,但是先执行')

打印结果:
在这里插入图片描述
发现 timeout() 函数虽然调用了,但是没打印 hello world!; 先不要着急, 看一看timeout() 返回了什么? 把上面的 timeout() 语句改为console.log(timeout())

打印结果:
在这里插入图片描述

原来async 函数返回的是一个promise 对象,并且Promise还有state和result,如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()方法把它转化成一个promise对象作为返回,但如果timeout函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象

async function timeout() {throw new Error('rejected');
}
console.log(timeout());

就会调用Promise.reject() 返回一个promise 对象
 在这里插入图片描述
那么要想获取到async 函数的执行结果,就要调用promise的then 或 catch 来给它注册回调函数

继续修改代码

  async function timeout() {return 'hello world!'}timeout().then(val => {console.log(val)})console.log('我虽然在后面,但是先执行')

打印结果:
在这里插入图片描述
我们获取到了"hello world!', 同时timeout的执行也没有阻塞后面代码的执行,和 我们刚才说的一致。
 
如果async 函数执行完,返回的promise 没有注册回调函数,比如函数内部做了一次for 循环,你会发现函数的调用,就是执行了函数体,和普通函数没有区别,唯一的区别就是函数执行完会返回一个promise 对象。

   async function timeout () {for (let index = 0; index < 3; index++) {console.log('async', +index)}}console.log(timeout())console.log('outer')

在这里插入图片描述

另外,async函数返回一个promise对象,下面两种方法是等效的

// 方法1
function f() {return Promise.resolve('TEST');
}// asyncF is equivalent to f!// 方法2
async function asyncF() {return 'TEST';
}

二. 关于await

1) await 到底在等啥?

async 关键字差不多了,最重要的就是async函数的执行会返回promise对象,并且把内部的值进行promise的封装。如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。
如果只是async,和promise差不多,但有了await就不一样了,await关键字只能放到async函数里面,await是等待的意思,那么它等待什么呢?它后面跟着什么呢?其实await不仅仅用于等Promise对象,还可以等任意表达式,所以await后面实际是可以接普通函数调用或者直接量的,不过我们更多的是放一个返回promise 对象的表达式。他等待的是promise对象执行完毕,并返回结果。

//所以下面这个示例完全可以正确运行function getSomething () {return 'something'}async function testAsync () {return Promise.resolve('hello async')}async function test () {const v1 = await getSomething()const v2 = await testAsync()console.log(v1, v2)}test()

2) await 等到了要等的,然后呢?

await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?

  1. 如果它等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。
  2. 如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。

3) async/await 帮我们干了啥?

做个简单的比较
现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写

function takeLongTime () {return new Promise(resolve => {setTimeout(() =>resolve('long_time_value'), 1000)})}
takeLongTime().then(val => {console.log(val, 'val')
})

如果改用 async/await 呢,会是这样

    function takeLongTime () {return new Promise(resolve => {setTimeout(() =>resolve('long_time_value'), 1000)})}async function test () {let v = await takeLongTime()console.log(v, 'v')}test()

眼尖的已经发现 takeLongTime () 没有申明为async。实际上takeLongTime () 本身就返回Promise对象,加不加async结果都一样。

4) await 优势在于处理 then 链,使代码看起来像同步代码一样,下面是实例应用

现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2

// 2s 之后返回双倍的值
function doubleAfter2seconds (num) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(num * 2)}, 2000)})}

现在再写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用

async function testResult() {let result = await doubleAfter2seconds(30);console.log(result); //2s后打印60
}
testResult();

代码的执行过程
调用testResult 函数,它里面遇到了await, await 表示等待,代码就暂停到这里,不再向下执行了,它等待后面的promise对象执行完毕,然后拿到promise resolve 的值并进行返回,返回值拿到之后,它继续向下执行。具体到 我们的代码, 遇到await 之后,代码就暂停执行了, 等待doubleAfter2seconds(30) 执行完毕,doubleAfter2seconds(30) 返回的promise 开始执行,2秒 之后,promise resolve 了, 并返回了值为60, 这时await 才拿到返回值60, 然后赋值给result, 暂停结束,代码继续执行,执行 console.log语句。

就这一个函数,我们可能看不出async/await 的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?

async function testResult() {let first = await doubleAfter2seconds(30);let second = await doubleAfter2seconds(50);let third = await doubleAfter2seconds(30);console.log(first + second + third);
}
testResult()

6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。

这里强调一下,当js引擎在等待promise.resolve的时候,他并没有真正的暂停工作,它可以处理其他的一些事情,如果我们在testResult函数后面继续执行其他代码,比如console.log一下,会发现console.log代码先执行。

async function testResult() {let first = await doubleAfter2seconds(30);let second = await doubleAfter2seconds(50);let third = await doubleAfter2seconds(30);console.log(first + second + third);
}
testResult()
console.log('我先执行!!!')

先输出 “我先执行!!!”,6s后输出计算结果。
在这里插入图片描述

三、举例

当遇到 await 时,会阻塞函数内部处于它后面的代码(而非整段代码),去执行该函数外部的同步代码;当外部的同步代码执行完毕,再回到该函数执行剩余的代码。并且当 await 执行完毕之后,会优先处理微任务队列的代码。

async function fn1 (){console.log(1)await fn2() // fn2进入微任务队列等待执行console.log(2) // 阻塞
}
async function fn2 (){console.log('fn2')
}
fn1()
console.log(3)// 输出结果:1,fn2,3,2

上面的例子中,await 会阻塞它下面的代码(即加入微任务队列),先执行 async 外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码。

总结:

  1. async 函数
    1)函数的返回值为Promise对象
    2)Promise对象的结果由async函数执行的返回值决定
  2. await 表达式
    1)正常情况下,await右侧的表达式一般为 promise对象 , 但也可以是其它的值
    2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
    3)如果表达式是其它值, 直接将此值作为await的返回值
  3. asyncawait基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。
  4. 在使用await的时候我们只是暂停了函数,而非整段代码。这里经常会是容易犯错的地方。
  5. async和await是非阻塞的
  6. 仍然可以使用 Promise,例如Promise.all(p1, p2, p3).,接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是,就会先调用 Promise .resolve方法,将参数转为 Promise 实例,再进一步处理。只要 p1、p2、p3 之中有一个被 rejected,整个状态就变成 rejected。
  7. 注意
    1)await必须写在async函数中, 但async函数中可以没有await
    2)如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理

=========================== 2023-3-3更新 ===========================

async/await or promise使用时到底选择哪个呢?

大部分复杂情况下async/await 的确是最优解,个人觉得也不是所有情况下都是 async/await 写起来更爽,最不爽的就是他的错误处理,try...catch这个代码看起来就很奇怪(当然也有很多人喜欢这种错误处理方式)。所以我个人的习惯,当只有一个异步请求,且需要做错误处理的情况下,更倾向于使用 promise。比如

// promise
getInfo().then(res => {//do somethings}).catch(err => {//do somethings})// async/await
try {const res = await getInfo()//do somethings
} catch (error) {//do somethings
}

在有嵌套请求的情况下,肯定是 async/await 更直观的。

// promise
a(() => {b(() => {c()})
})// async/await
await a()
await b()
await c()

当然代码写的好与不好还是取决于写代码的人的。
比如一个常见的业务场景:有两个并发的异步请求,在都完成后do something。但很多人会错误的用串行的方式实现了。

//错误
await a()
await b()
//这样变成了 a().then(() => b() )
// a 好了才会执行 b
done()//正确
await Promise.all([a(), b()])
done()

还有一个小细节async/await打包后的代码其实会比 promise 复杂很多, 当然这个是一个忽略不计得问题。

总结:
我认为它们两个人并不是 or 的关系,在特定的业务场景下,选择相对而言代码可读性更好地解决方案。具体该怎么选择还是需要大家更具自己团队的风格或者自己的理解来判断。

其实围绕async/await,还有更深入的知识点,个人感觉还蛮有趣的,感兴趣的可以继续阅读我的其它文章。
理解JS的事件循环机制(Event Loop)
理解js中的同步和异步

学习过程中参考了:
用 async/await 来处理异步
vue中异步函数async和await的用法
理解 JavaScript 的 async/await


http://chatgpt.dhexx.cn/article/RWyAKm2q.shtml

相关文章

(一)概述:NGS测序在病原微生物检测中的应用

❝ NGS 技术在临床上的应用逐步趋于成熟&#xff0c;从早期的肿瘤基因检测&#xff0c;到如今大热的微生物病原核酸检测&#xff0c;NGS 技术以其快速、准确和高分辨率的特点&#xff0c;发挥着无可替代的作用。 ❞ 微生物在地球上无处不在&#xff0c;从陆地到海洋&#xff0c…

肿瘤NGS测序公司生信工程师水平划分

生信工程师能力划分 1、大头兵&#xff08;1&#xff09;初级生信工程师工作职责任职要求 &#xff08;2&#xff09;中级生信工程师工作职责任职要求 &#xff08;3&#xff09;高级生信工程师工作职责任职要求 2、主管/经理职位描述任职要求 3、总监参考资料&#xff1a; 去年…

NGS数据分析实践:04. 准备测序数据

NGS数据分析实践&#xff1a;04. 准备测序数据 文接上篇&#xff1a;NGS数据分析实践&#xff1a;03. 涉及的常用数据格式[5] - vcf格式 本次NGS数据&#xff0c;采用多重PCR靶向扩增子测序技术&#xff0c;在Illunima Hiseq X Ten测序平台上进行双末端&#xff08;paired-end…

NGS_panel的CAP认证学习笔记

对于基因的定义总体可以划分为两类 GAD: Gene associated with Mendelian disorder; GADs include genes that meet criteria for definitive, strong, or moderate evidence for association with disease as described by ClinGenGUS: Gene of uncertain significance; GUSs …

45万例患者基因检测显示:NGS很难检测出七分之一的致病变异

基于下一代测序&#xff08;NGS&#xff09;的临床基因测试越来越多地用于辅助诊断&#xff0c;针对该技术的临床应用有具体的指南&#xff0c;除了检测相对可靠的单核苷酸变异&#xff08;SNVs&#xff09;和微小插入缺失&#xff08;indel&#xff09;&#xff0c;NGS也已经被…

R实战 | NGS数据时间序列分析(maSigPro)

masigpro 跟着Cell学作图 | 6.时间序列分析(Mfuzz包) 一个答疑教程。 maSigPro 流程 示例数据 #BiocManager::install(maSigPro) library(maSigPro) # 载入示例数据 data(data.abiotic) data.abiotic[1:5,1:5] data(edesign.abiotic) head(edesign.abiotic) > data.abiotic…

NGS数据分析实践:03. 涉及的常用数据格式[5] - vcf格式

NGS数据分析实践&#xff1a;03. 涉及的常用数据格式[5] - vcf格式 6. vcf格式6.1 vcf格式整体描述6.2 第8列INFO详解6.3 第9列FORMAT详解6.4 vcf文件简单解读 系列文章&#xff1a; 二代测序方法&#xff1a;DNA测序之靶向重测序 NGS数据分析实践&#xff1a;00. 变异识别的基…

生信小白学习日记Day4Day5——NGS基础 NGS分析注释(BWA软件)

2019年5月30日&#xff0c;晚上&#xff0c;心情变好&#xff0c;好几天没更新了&#xff0c;看到男朋友在学一款软件&#xff0c;我也近朱者赤&#xff0c;来继续注释Day2-2中NGS分析流程中的一个重要软件——BWA NGS基础 NGS分析注释 BWA 对应于NGS分析流程的这两步&…

NGS数据分析实践:00. 变异识别的基本流程

NGS数据分析实践&#xff1a;00. 变异识别的基本流程 变异识别过程可以分成3大块&#xff1a;1. 原始数据质控&#xff1b;2. 数据预处理&#xff1b;3. 变异识别。大致可以细分为6个部分&#xff1a;(1) 原始测序数据的质控&#xff1b;(2) read比对&#xff0c;排序和标记PCR…

如何用软件模拟NGS数据

如何用软件模拟NGS数据 为了评价一个工具的性能&#xff0c;通常我们都需要先模拟一批数据。这样相当于有了参考答案&#xff0c;才能检查工具的实际表现情况。因此对于我们而言&#xff0c;面对一个新的功能&#xff0c;可以先用模拟的数据测试下不同工具的优缺点。有如下几个…

生信小白学习日记Day2——NGS基础 illumina高通量测序原理

2019年5月26日&#xff0c;周日&#xff0c;小雨 说明&#xff1a;阅读生信宝典和查阅文章的总结&#xff0c;原文请关注公众号生信宝典&#xff0c;参考的博文都附有链接&#xff0c;仅供参考。 生信宝典 NGS基础——高通量测序原理 本文介绍了测序文库构建原理、链特异性文…

NGS数据分析实践:05. 测序数据的基本质控 [2] - MultiQC

NGS数据分析实践&#xff1a;05. 测序数据的基本质控 [2] - MultiQC 2. MultiQC2.1 帮助信息及运行代码2.2 报告解读2.3 小结 文接上篇&#xff1a;NGS数据分析实践&#xff1a;05. 测序数据的基本质控 [1] - FastQC 2. MultiQC NGS技术的进步催生了新的实验设计、分析类型和极…

NGS数据分析实践:03. 涉及的常用数据格式[2] - sam/bam格式

NGS数据分析实践&#xff1a;03. 涉及的常用数据格式[2] - sam/bam格式 2. sam和bam格式 系列文章&#xff1a; 二代测序方法&#xff1a;DNA测序之靶向重测序 NGS数据分析实践&#xff1a;00. 变异识别的基本流程 NGS数据分析实践&#xff1a;01. Conda环境配置及软件安装 NGS…

NGS数据过滤之trimmomatic

NGS 原始数据过滤对后续分析至关重要&#xff0c;去除一些无用的序列也可以提高后续分析的准确率和效率。Trimmomatic 是一个功能强大的数据过滤软件。 Trimmomatic 介绍 Trimmomatic 发表的文章至今已被引用了 2810 次&#xff0c;是一个广受欢迎的 Illumina 平台数据过滤工具…

NGS基础:测序原始数据批量下载

生物或医学中涉及高通量测序的论文&#xff0c;一般会将原始测序数据上传到公开的数据库&#xff0c;上传方式见测序文章数据上传找哪里&#xff1b;并在文章末尾标明数据存储位置和登录号,如 The data from this study was deposited in NCBI Sequence Read Archive under acc…

NGS之数据格式

生物信息中常见的几种数据格式有:fasta、fastq、bam、sam、vcf、bed、gff。 参考&#xff1a;http://www.biotrainee.com/thread-42-1-1.html FASTQ 参考&#xff1a;https://en.wikipedia.org/wiki/FASTQ_format fastq格式是文本格式。它有对应序列字符的质量分数&#xff…

生信小白学习日记Day3——NGS基础 NGS分析注解(质量分析软件)

2019年5月27日&#xff0c;天气舒适&#xff0c;忙碌一天之后开始今天的生信学习。今天就昨天Day2-2的一些标记加以查询说明&#xff0c;仅供参考。 NGS基础 NGS分析注解 1. 质量分析软件 昨天提到&#xff0c;拿到数据后可以通过一些软件来评估测序质量的好坏&#xff0c;…

NGS 数据过滤之 Trimmomatic

NGS Trimmomatic 支持多线程&#xff0c;处理数据速度快&#xff0c;主要用来去除 Illumina 平台的 Fastq 序列中的接头&#xff0c;并根据碱基质量值对 Fastq 进行修剪。软件有两种过滤模式&#xff0c;分别对应 SE 和 PE 测序数据&#xff0c;同时支持 gzip 和 bzip2 压缩文…

NGS基础名词解释(1)

什么是高通量测序&#xff1f; 高通量测序技术&#xff08; High-throughput sequencing &#xff0c; HTS &#xff09;是对传统 Sanger 测序&#xff08;称为一代测序技术&#xff09;革命性的改变 , 一次对几十万到几百万条核酸分子进行序列测定 , 因此在有些文献中称其为…

【评测】NGS建库试剂盒

NGS建库试剂 一、基本信息&#xff1a; 1、产品名称&#xff1a;SynplSeq DNA Library Prep Kit for Illumina 2、货号及规格 3、保存条件&#xff1a;-20℃ 二、产品描述&#xff1a; 1、产品介绍 文库构建是NGS测序的关键环节。SynplSeq DNA Library Prep Kit for illu…