前言
在ES7之前,了解到Promise是ES6为了解决异步回调而产生的解决方案,避免出现回调地狱(Callback Hell),那么ES7为什么又提出了新的Async/Await标准?问题答案就是:Promise虽然解决了异步嵌套的怪圈,使用表达清晰的链式表达;但是如果在实际开发过程中有些地方有大量的异步请求的时候,而且流程复杂嵌套的情况下,检查相关代码会发现一个比较尴尬的问题:无从下手。
一、Async/Await是什么?
Async/Await是基于Promise而来的,Async/Await是相互依存的,缺一不可,它们的出现就是为了Promise而来,也算是Promise的进化改良版,为的就是解决文章开始说的如果出现大量复杂嵌套不易读的Promise异步问题。
1、Async/Await基本含义
①Async/Await是基于Promise实现的,是写异步代码的新方式,它们不能用于普通的回调函数;
②Async/Await也是非阻塞的;
③Async/Await的写法使得异步代码看起来像同步代码,简洁、便于读取。
2、Async/Await的语法
async必须声明的是一个function函数,await就必须是在async声明的函数内部使用,这是一个固定搭配,任何一种不符合这两个条件,程序就会报错,具体举实例来直观看一下:
let data = 'data'
a = async function () {
const b = function () {
await data
}
}
二、Async/Awaitd的本质
1、 Async的本质
async是对generator的再一次语法糖封装,帮助实现了生成器的调用,使语句更贴近同步代码的表达方式,可以将async函数看做是多个异步操作封装的 promise对象。
async声明的函数的返回其实就是一个Promise,也就是说只要声明的函数是async,不管内部怎么处理,它返回的一定是一个Promise,举个例子如下所示:
(async function () {
return 'Promis+++‘
})() // 返回的是Promise+++
2、 Awaitd的本质
await的本质其实是可以提供等同于“同步效果”的等待异步返回能力的语法糖,也就是then的语法糖。如果想使用await来执行一个异步操作,那么其调用函数必须使用async来声明。
await能返回一个Promise对象,也能返回一个值。若await返回的是Promise对象,那么还可以继续给await的返回值使用then函数。举个实例看一下:
const a = async ()=>{
let message = '声明值111’;
let result = await message;
console.log('由于上面的程序还没执行完,“等待一会”');
return result;
}
a().then(result=>{
console.log('输出',result);
})
三、 Async/Await的优势
为什么说Async/Awaitd比Promise更胜一筹?具体原因如下所示。
1、 简洁明了
根据上述关于Async/Awaitd的实例可以看到,Async/Awaitd的写法很简单,相比Promise的写法,不用写.then,不用写匿名函数处理Promise的resolve值,也不用定义多余的data变量,更避免了嵌套代码的操作,大大省去了很多代码行,使得处理异步操作的代码简洁明了,方便查阅和精准定位。
2、 错误处理的方式
Async/Await可以让try/catch同时处理同步和异步的错误,而且在Promise中try/catch不能处理JSON.parse的错误,在Promise中需要使用.catch,但是错误处理的那坨代码会非常冗余,要知道实际开发过程中代码会比理论上的情况会更复杂。
通过使用Async/Await,try/catch能处理JSON.parse的错误,具体实例如下所示:
const request = async () => {
try {
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
3、 条件语句
通过使用Async/Await,可以使得条件语句写法简洁又可以提高代码可读性,这里就不再举对比的例子,只举一个Async/Await的使用实例来说:
const request = async () => {
const data = await getJSON()
if (data.anotherRequest) {
const moreData = await anotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
4、 中间值
在实际开发过程中会遇到这种场景:调用promise1,使用promise1返回的结果再去调用promise2,然后使用两者的结果去调用promise3。在没有使用Async/Await之前的写法,应该是这样的:
const request = () => {
eturn promise1()
.then(value1 => {
return promise2(value1)
.then(value2 => {
return promise3(value1, value2)
})
})
}
使用了Async/Await的写法之后,是这样的:
const request = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
通过上述两个写法,直观的看出来使用Async/Await之后会使得代码变得非常整洁简单,直观,高可读性。
5、 错误栈对比
如果实例中调用多个Promise,其中的一个Promise出现错误,Promise链中返回的错误栈没有显示具体的错误发生的位置信息,这就造成排查错误的耗时时长和解决的难度,甚至会起到反作用,假设错误栈的唯一函数名为errorPromise,但是它和错误没有直接关系。如果使用了Async/Await之后,错误栈会直接指向错误所在的函数,这样更清晰直观的方便排查问题所在,尤其是在查看分析错误日志的时候非常有效有用。
6、 调试
通过上面描述的Async/Await优点中,一直在反复强调Async/Await会使得代码简洁明了,其实在调试过程中,Async/Await也可以使得代码调试起来很轻松简单,相对于Promise来讲,不用再写太多箭头函数,可以直接像调试同步代码一样单步走,跳过await语句。
7、 中断/终止程序
首先要明确知道,Promise自身是不能终止的,Promise本身只是一个状态机,存储了三种状态,一旦进行发出请求,就必须要闭环,无法进行取消操作,就算是在前面讲到的pending状态,也只是一个挂起请求的状态,但不是取消。
但是使用Async/Await的时候,想要终止程序就很简单,那是因为Async/Await语义化很明显,和一般的function的写法类似,想要终端程序的时候,直接return一个值(“”、null、false等)就可以了,实际上就是直接返回一个Promise。具体实例如下所示:
let count = 3;
const a = async ()=>{
const result = await delay(2000);
const result1 = await delaySecond(count);
if (count > 2) {
return '';
// return false;
// return null;
}
console.log(await delay(2000));
console.log(‘结束’);
};
a().then(result=>{
console.log(result);
})
.catch(err=>{
console.log(err);
})
async函数本质就是返回一个Promise。
四、实际开发过程中异步操作需要注意的事项
Async/Await使用for循环获取数据(串行)
根据上面Promise的for循环获取数据来做对比,直接使用上述实例的场景,来看看Async/Await的写法,具体操作如下所示:
(async ()=>{
array = [timeout(2000), timeout(1000), timeout(1000)]
for (var i=0; i < array.length; i++) {
result = await array[i]();
console.log(result);
}
})()
最后
通过对比Promise,在这里还要夸一下Async/Await,直观的可以看到同样的需求,使用Async/Await来实现是不是非常的方便和简洁。而且在前端面试求职的时候该知识点也是必问知识点,其重要性不言而喻,这里就不再赘述。以上就是本章全部内容,欢迎关注!