首页 > 编程语言 >JavaScript中的async/await

JavaScript中的async/await

时间:2024-06-07 19:22:21浏览次数:17  
标签:await console JavaScript resolve Promise result async

  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。

标签:await,console,JavaScript,resolve,Promise,result,async
From: https://www.cnblogs.com/cnyjh/p/18237759

相关文章

  • .net core 使用js,.net core 使用javascript,在.net core项目中怎么使用javascript
    .netcore使用js,.netcore使用javascript,在.netcore项目中怎么使用javascript我项目里需要用到“文字编码”,为了保证前端和后端的编码解码不处bug,所以,我在项目中用了这个下面推荐之前在.netF4.0时的方法文章一:c#解码js的escape_c#escape-CSDN博客文章二:c#对js......
  • 螺旋转动,矩阵的舞蹈:JavaScript中实现螺旋矩阵遍历算法
    螺旋转动,矩阵的舞蹈:JavaScript中实现螺旋矩阵遍历算法基础概念:什么是螺旋矩阵?核心算法解析示例一:基础螺旋矩阵遍历算法解析进阶技巧示例二:动态生成螺旋矩阵技巧点实战与性能优化问题与解决:大矩阵处理结语与讨论在编程的奇幻世界里,数组与矩阵是构筑数字城堡的基石......
  • 数组大扫雷行动:JavaScript中的高效移除指定元素
    数组大扫雷行动:JavaScript中的高效移除指定元素基本概念:移除元素,何为?方法一:splice()大法方法二:filter()轻功功能使用角度与技巧案例一:简单移除案例二:条件移除实战分析遇到的坑与对策结语与讨论在JavaScript编程的征途中,数组是我们的常伴,而“移除元素”这一任务,则像......
  • 【JS封装-数组操作】强化编程实践:精选JavaScript函数封装集锦-关于数组操作(数组去重、
    目录数组去重数组快速排序过滤数组映射数组数组扁平化数组求和数组最大值数组最小值数组切片数组乱序(洗牌算法)数组去重/***去除数组中的重复项。*@param{Array}array要去重的数组。*@returns{Array}去重后的数组。*/functionuniqueArray(array......
  • JavaScript第十一讲:DOM编程“事件”
    前言当涉及到DOM(DocumentObjectModel)的事件时,JavaScript提供了许多内置的事件处理器,用于处理用户在网页上执行的各种操作。1.焦点事件当元素获得或失去焦点时触发。onfocus:当元素获得焦点时触发。onblur:当元素失去焦点时触发。//假设有一个输入框<inputtype="......
  • 【JavaScript函数】
    函数:是被设计为执行特定任务的代码块。作用:代码重用,结构清晰1.1、定义函数定义函数有两种方法:函数声明和函数表达式:1.1.1、函数声明解释:这里声明一个函数f,并传入一个参数a,当函数执行以后,通过return关键字返回了a+1的值。1.1.2、函数表达式通过定义一个匿......
  • 微前端学习笔记(3):前端沙箱之JavaScript的sandbox(沙盒/沙箱)
    sandboxSandbox(沙盒/沙箱)的主要目的是为了安全性,以防止恶意代码或者不受信任的脚本访问敏感资源或干扰其他应用程序的执行。通过在沙盒环境中运行,可以确保代码的行为被限制在一个安全的范围内,防止其超出预期权限进行操作。沙箱(Sandbox)是一种安全机制,目的是让程序运行在一个相对......
  • 【网络安全的神秘世界】JavaScript
    ......
  • 【JavaScript详解】Day02
    JavaScript基础-第2天理解什么是流程控制,知道条件控制的种类并掌握其对应的语法规则,具备利用循环编写简易ATM取款机程序能力运算符语句综合案例运算符算术运算符数字是用来计算的,比如:乘法*、除法/、加法+、减法-等等,所以经常和算术运算符一起。算术运算......
  • JavaScript-变量
    JavaScript-1.变量1.js引入方式a.内部脚本:将Js代码定义在script标签中 <script>window.alert("HELLOJS")//将我们指定的入参文案以弹框的形式显示出来document.write("hellojs!")//将入参文案写入到当前的HTML内容中console.log("helloj......