JavaScript中的async/await

news/2025/1/22 21:09:00/文章来源:https://www.cnblogs.com/cnyjh/p/18237759
  1. async/await是什么?
    async 是一个修饰符,async 定义的函数会默认的返回一个Promise对象resolve的值,因此对async函数可以直接进行then操作,返回的值即为then方法的传入函数。

await 也是一个修饰符,await 关键字 只能放在 async 函数内部, await关键字的作用 就是获取 Promise中返回的内容, 获取的是Promise函数中resolve或者reject的值。

那么async/await到底是干嘛的呢?

1.async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。
2.async/await 是建立在 promise 的基础上。(如果对Promise不熟悉,我已经着手在写Promise的文章了)
3.async/await 像 promise 一样,也是非阻塞的。
4.async/await 让异步代码看起来、表现起来更像同步代码。这正是其威力所在。

async/await其实是Promise的语法糖,它能实现的效果都能用then链来实现,这也和我们之前提到的一样,它是为优化then链而开发出来的。从字面上来看,async是“异步”的简写,await译为等待,所以我们很好理解async声明function是异步的,await等待某个操作完成。当然语法上强制规定await只能出现在asnyc函数中,我们先来看看async函数返回了什么:
async function testAsy(){
return 'hello world';
}
let result = testAsy();
console.log(result)


这个async声明的异步函数把return后面直接量通过Promise.resolve()返回Promise对象,所以如果这个最外层没有用await调用的话,是可以用原来then链的方式来调用的:
async function testAsy(){
return 'hello world'
}
let result = testAsy()
console.log(result)
result.then(v=>{
console.log(v) //hello world
})
联想一下Promise特点——异步无等待,所以当没有await语句执行async函数,它就会立即执行,返回一个Promise对象,非阻塞,与普通的Promise对象函数一致。

以下是一个promise在1s之后resolve的例子:
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done!'), 1000)
})
let result = await promise // 直到promise返回一个resolve值(*)
console.log(result)
}
f()

函数执行到(*)行会‘暂停’,当promise处理完成后重新恢复运行, resolve的值成了最终的result,所以上面的代码会在1s后输出’done!’

我们强调一下:await字面上使得JavaScript等待,直到promise处理完成,

然后将结果继续下去。这并不会花费任何的cpu资源,因为引擎能够同时做其他工作:执行其他脚本,处理事件等等。

这只是一个更优雅的得到promise值的语句,它比promise更加容易阅读和书写。

如果去掉await,我们获得的就是一个promise对象。

//加上await
done!

//没有await
Promise {}
proto: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "done!"

重点就在await,它等待什么呢?

按照语法说明,await等待的是一个Promise对象,或者是其他值(也就是说可以等待任何值),如果等待的是Promise对象,则返回Promise的处理结果;如果是其他值,则返回该值本身。并且await会暂停当前async function的执行,等待Promise的处理完成。若Promise正常处理(fulfillded),其将回调的resolve函数参数作为await表达式的值,继续执行async function;若Promise处理异常(rejected),await表达式会把Promise异常原因抛出;另外如果await操作符后面的表达式不是一个Promise对象,则返回该值本身。

  1. 深入理解async/await
    我们来详细说明一下async/await的作用。await操作符后面可以是任意值,当是Promise对象的时候,会暂停async function执行。也就是说,必须得等待await后面的Promise处理完成才能继续:
    function testAsy(x){
    return new Promise(resolve=>{setTimeout(() => {
    resolve(x);
    }, 3000)
    }
    )
    }
    async function testAwt(){
    let result = await testAsy('hello world');
    console.log(result); // 3秒钟之后出现hello world
    }
    testAwt();

await 表达式的运算结果取决于它等的东西。

如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

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

我们再把上面的代码修改一下,好好体会“阻塞”这个词

function testAsy(x){
return new Promise(resolve=>{setTimeout(() => {
resolve(x);
}, 3000)
}
)
}
async function testAwt(){
let result = await testAsy('hello world');
console.log(result); // 3秒钟之后出现hello world
console.log('Y') // 3秒钟之后出现Y
}
testAwt();
console.log('L') //立即输出L

这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。await暂停当前async的执行,所以’tangSir’'最先输出,hello world’和‘tangj’是3秒钟后同时出现的。

为什么会立即输出L,这就涉及到了JS中的事件循环了,总的来说,异步函数会在非异步函数之后运行。

  1. async和await简单应用
    上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

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

function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
}

takeLongTime().then(v => {
console.log("got", v); //一秒钟后输出got long_time_value
});

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

function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
}

async function test() {
const v = await takeLongTime();
console.log(v); // 一秒钟后输出long_time_value
}

test();

ankLongTime()本身就是返回的 Promise 对象,所以加不加 async结果都一样。

  1. 处理then链
    前面我们说了,async和await是处理then链的语法糖,现在我们来看看具体是怎么实现的:

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用setTimeout来模拟异步操作:

/**

  • 传入参数 n,表示这个函数执行的时间(毫秒)
  • 执行的结果是 n + 200,这个值将用于下一步骤
    */
    function takeLongTime(n) {
    return new Promise(resolve => {
    setTimeout(() => resolve(n + 200), n);
    });
    }

function step1(n) {
console.log(step1 with ${n});
return takeLongTime(n);
}

function step2(n) {
console.log(step2 with ${n});
return takeLongTime(n);
}

function step3(n) {
console.log(step3 with ${n});
return takeLongTime(n);
}

现在用 Promise 方式来实现这三个步骤的处理。

function doIt(){
console.time('doIt');
let time1 = 300;
step1(time1)
.then((time2) => step2(time2))
.then((time3) => step3(time3))  
.then((result) => {
console.log(result is ${result});
console.timeEnd("doIt");
})
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1510.2490234375ms

输出结果 result 是 step3() 的参数 700 + 200 = 900。doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

如果用 async/await 来实现呢,会是这样:

async function doIt() {
console.time('doIt');
let time1 = 300;
let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
let time3 = await step1(time2);
let result = await step1(time3);
console.log(result is ${result});
console.timeEnd('doIt');
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1512.904296875ms

显然我们用async/await简单多了。

  1. Promise处理结果为rejected
    await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。

async function myFunction() {
try {
await somethingThatReturnAPromise();
} catch (err){
console.log(err);
}
}

//另一种写法
async function myFunction() {
await somethingThatReturnAPromise().catch(function(err) {
console.log(err);
})
}

6.实例
Vue普通写法:

methods: {
getLocation(phoneNum) {
return axios.post('/mm接口', {
phoneNum
})
},
getFaceList(province, city) {
return axios.post('/nn接口', {
province,
city
})
},
getFaceResult () {
this.getLocation(this.phoneNum).then(res => {
if (res.status === 200 && res.data.success) {
let province = res.data.obj.province;
let city = res.data.obj.city;
this.getFaceList(province, city).then(res => {
if(res.status === 200 && res.data.success) {
this.faceList = res.data.obj
}
})
}
}).catch(err => {
console.log(err)
})
}
}

这时你看到了then 的链式写法,有一点回调地域的感觉。现在我们在有async/ await 来改造一下。

加入async/ await:

首先把 getFaceResult 转化成一个async 函数,就是在其前面加async, 因为它的调用方法和普通函数的调用方法是一致,所以没有什么问题。然后就把 getLocation 和getFaceList 放到await 后面,等待执行, getFaceResult 函数修改如下:

async getFaceResult () {
let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {
this.faceList = result.data.obj;
}
}
}

现在代码的书写方式,就像写同步代码一样,没有回调的感觉,非常舒服。

现在就还差一点需要说明,那就是怎么处理异常,如果请求发生异常,怎么处理? 它用的是try/catch 来捕获异常,把await 放到 try 中进行执行,如有异常,就使用catch 进行处理。

async getFaceResult () {
try {
let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {
this.faceList = result.data.obj;
}
}
} catch(err) {
console.log(err);
}
}

注:
ES7引入的关键字async/await是对JavaScript异步编程的改进。它可以使代码更容易阅读和调试。然而,为了正确使用它们,必须完全理解promise,因为它们只不过是语法糖,而潜在的技术仍然是promise。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql 8.4.0 结合 Docker 搭建GTID主从复制,以及传统主从复制

注意:本教程不适用旧版本,Mysql 8.4.0 和 旧版本,主从复制相关命令有所变化,具体区别请看文末参考 软件版本 Docker:26.1.3 Mysql:8.4.0GTID主从复制 1.准备主从两台服务器 2.两台服务器分别创建DockerCompose文件 services:mysql:image: mysql:8.4.0ports:- "3306:…

Vue Router 4与路由管理实战

这篇文章介绍了如何在Vue.js应用中利用Vue Router实现单页面应用的路由管理,包括配置路由、导航守卫的使用、路由懒加载以优化性能以及动态路由的实现方法,旨在提升用户体验和应用加载效率title: Vue Router 4与路由管理实战 date: 2024/6/7 updated: 2024/6/7 excerpt: 这篇…

Body SweptSolid CompositeCurve Geometry

Body SweptSolid CompositeCurve Geometry 下图显示了应用此概念时使用的泛型类和关系。此外,概念可能对通用或标准化的行业实践和场景具有特别重要的意义。对于这些特定的使用场景,下表显示了用户可能采用的一般使用模式的推荐列表。 #####################################…

设备树学习

设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、 IIC 接口上接了哪些设备、 SPI 接口上接了哪些设备等等。具体如下图…

平稳交付 20+ 医院,卓健科技基于 OpenCloudOS 的落地实践

本文将会阐述卓健科技运用 OpenCloudOS 的背景情况,深入探究其背后的缘由以及详细的实践流程。导语:随着数字化转型于各个行业领域当中持续地深入推进,充当底层支撑的操作系统正发挥着愈发关键且重要的作用。卓健科技把 OpenCloudOS 当作首要的交付系统,达成了项目交付速度…

机器学习笔记(2): Logistic 回归

Logistic 回归是线性回归中一个很重要的部分。 Logistic 函数: \[\sigma(x) = \frac {L} {1 + \exp(-k(x - x_0))} \]其中:\(L\) 表示最大值 \(x_0\) 表示对称中心 \(k\) 表示倾斜度一般来说,都将 \(L\) 设为 \(1\),而 \(k\) 和 \(x_0\) 在参数中控制。认为特征只有一个,那…

AI智能助手(web端和h5端)

需要在平台右下角设置一个图标,点击后可弹出智能助手弹框,同时不影响平台其他操作,支持流式文本(sse)、图文回复展示 web端 ,效果如下图: h5端 效果:vue3 + element-ui 实现 仿AI智能机器人助手h5, 支持流式文本(sse)、图文回复展示 web端源码:https://github.c…

dos窗口中关于目录和文件的操作

1 将输入内容保存到文件 dir > c:\1.txt 2 列出当前目录的所有文件 dir /a-d 2.1 列出当前目录的所有文件 dir /a-d /d 2.12列出当前目录及子目录下的所有文件 dir /a-d /d /s 3 列出当前目录下的所有目录 dir /ad /d /s 3.1 列出当前目录下的所有目录 dir /ad /d

【网络调试工具】wriesharktcpdump

TcpDump tcpdump抓包命令 网络报文的参数非常多,在实际抓包的时候都是采用条件过滤的选项来获取我们关心的报文。 1.基于IP地址过滤:host tcpdump host 192.168.10.100数据包的ip可以细分为源ip和目标ip两种: # 根据源ip进行过滤 tcpdump -i eth2 src 192.168.10.100 # 更具…

[UE 虚幻引擎] DTLoadFbx 运行时加载FBX本地模型插件说明

本插件可以在打包后运行时动态加载FBX模型。 新建一个Actor 并添加一个 DT Runtime Fbx Component。然后直接调用组件的函数 LoadFile 加载显示模型(注:不支持模型动画)FilePath : 加载模型的绝对路径。Create Collision : 是否创建碰撞体。本组件是继承于 UProceduralMeshC…