前边回答了关于Promise的问题,又有同学提出了新的问题:async和await是干什么用的?这节课咱们来解释一下。
async
学习了Promise后,我们可以便可以像这样创建一个异步函数:
function fn(){
return new Promise(resolve => {
resolve(10)
})
}
上例中,函数功能比较简单,即调用函数后返回了一个数字10(通过Promise异步返回)。但是不管返回什么样的结果,异步函数本身的返回值必须得是Promise才能称得上是一个异步函数。但是很明显,如果你的异步函数只是返回一个单个的值(比如10)却因此要编写创建Promise的一大串代码显然是划不来的。
为了解决这个问题,JS为我们提供了async关键字。只需要在创建函数时,在function前使用async关键字,即可使一个普通函数升级为异步函数,像是这样:
async function fn2(){
return 10
}
上例中fn2使用了async来声明,此时fn2变成为了一个异步函数。异步函数的第一个特点便是它的返回值会自动被包装为一个Promise,上例中fn2的返回值虽然是10,但是如果你尝试去接收的话,你会发现它返回的是一个Promise。这样看来,fn1和fn2的功能是一模一样的,不同点在于fn1的Promise是我们手动创建的,而fn2的却是自动创建的。
简而言之,async简化了异步函数的创建,省略了部分Promise的创建工作。但如果仅仅是这样那async的功能就显得十分的鸡肋了,因为这种简化只适用于单个返回结果的异步函数,如果函数功能比较复杂依然还是需要手动创建Promise。所以async还有第二个特点:在async声明的函数中可以使用await关键字。这是怎么回事?不急,下一小节我们介绍,这里先总结一下async的特点:
- async创建的函数其返回值会自动封装到一个Promise中返回
- async创建的函数中可以使用await关键字
await
调用一个异步函数时,我们通常得这样:
fn2()
.then(num => console.log(num))
.catch(err => console.log("出错了"))
这种方式并不优雅,且当我们需要同时调用多个异步函数时,我们必须要一直then下去,这样异步函数的调用就变得更加的不优雅了:
async function fn2(){
return 10
}
async function fn3(num){
return num + 5
}
async function fn4(num){
return num + 10
}
async function fn5(num){
return num + 20
}
fn2()
.then(fn3)
.then(fn4)
.then(fn5)
.then(console.log)
.catch(err => console.log("出错了"))
当你异步用得久了,你会发现你非常的怀念之前同步代码的编写方式,一行一行的,顺序特别的清晰,逻辑也比较容易理解,像是这样:
let result = fn2()
result = fn3(result)
result = fn4(result)
result = fn5(result)
console.log(result)
但是不行啊,上边的几个函数都是异步函数,返回值都是Promise,我这么写等于是将Promise作为参数传递,虽然能够执行,但是结果肯定不对啊!嘿嘿,为了解决我们这些既喜欢同步代码的编写方式,又想要使用异步的人,JS为我们提供了一个await关键字,让我们的理想照进了现实。
调用异步函数时除了可以通过then方法来读取结果以外,我们也可以直接在调用时使用await关键字,像是这样:
let result = await fn2()
这样调用后,异步函数会在有执行结果后,将执行结果返回(如果有错误就自动报错)。但是,你千万不要着急去尝试这行代码,因为这种方式一眼看上去就不对!fn2是异步的,它不会立刻返回结果,正常来讲它会在其他代码(它下边的代码)执行完才会返回结果,所以它应该返回的是一个Promise而不应该是一个有效值。如果它真的返回了一个有效的值,这也就意味着,它返回结果前,其后的代码是不能执行的,那么这也就失去了异步本身的意义了(会阻塞其他代码的执行)!
所以注意了await并不能任意的使用,因为await并不会改变异步的本质,它只是改变了异步的调用方式,让我们像编写同步代码一样去使用异步。而不是将异步改回到同步(真是这样的话就得不偿失了)。那么在哪里能够使用await呢?有两个地方:
- async关键字创建的函数
- JS模块的最外层作用域
像这样:
async function fn6(){
try{
let result = await fn2()
result = await fn3(result)
result = await fn4(result)
result = await fn5(result)
console.log(result)
}catch(e){
console.log("出错了")
}
}
fn6()
或者是匿名的异步函数:
(async ()=>{
try{
let result = await fn2()
result = await fn3(result)
result = await fn4(result)
result = await fn5(result)
console.log(result)
}catch(e){
console.log("出错了")
}
})()
或者是JS模块中:
<script type="module">
try{
let result = await fn2()
result = await fn3(result)
result = await fn4(result)
result = await fn5(result)
console.log(result)
}catch(e){
console.log("出错了")
}
</script>
await的执行原理并不难理解,因为await必须写在异步函数中或模块中,而异步函数和模块本身就是异步的,所以当执行到await的代码时,异步函数(或模块)会暂停执行,等到await标识的异步代码有结果时在继续执行函数(或模块)。
因为调用方式的改变,错误的处理方式也不再是catch方法,而是在同步调用中所使用的try-catch。
学习完了await我们也就理解了,async的第二个作用,相较于简化异步函数的创建能够使用await才是async函数的最大作用。当然无论是await调用还是直接调用,并没有本质区别,只是调用方式的不同,所以具体使用哪种,看你自己!
下载地址:
链接:https://pan.baidu.com/s/14Cqjzx4I_TMjeFf4GBIkBg?pwd=yvft
提取码:yvft
太棒了
bucuo