这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
问题
上一篇我们学习了Generator函数,不怕面试官的询问了。 但是再来看看这个场景:
面试官问:你能讲讲什么是Generator函数吗?
面试者:巴拉巴拉一大堆。。。,现在经常用的async函数就是Generator函数的改进版本。
面试官: 那你能讲讲async函数吗?
面试者: 。。。
以上这个场景纯属虚构,应该不会雷同。因为大家对async函数肯定比Generator函数熟悉,哈哈哈。
下面讲讲async函数的语法以及使用。
async函数
async函数一般是和await一起配合使用,跟Generator函数的*和yield很相似。
在function前面加上async关键字,代表就是async函数。然后里面使用await关键字。执行的时候遇到await关键字,先执行await后面的函数,执行完成才会继续执行下面的,依此类推。
async function fn () {
await fn1()
await fn2()
await fn3()
}
fn()
这个函数fn执行是顺序执行,fn1先执行完,再轮到fn2执行完,最后再执行fn3。任何一个函数卡住了都不会执行后面的函数。这对于一些操作需要依赖前面的结果的函数非常有用。
async function fn1(ms = 1000) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms)
})
}
async function fn () {
await fn1()
await fn1()
await fn1()
console.log(1)
}
fn()
上面的1是3个fn1都执行完再打印,所以结果是等待3s后再输出1。
await
在async函数,await可以跟着promise对象,也可以是基本类型。如果不是promise对象,会调用Promise.resolve转成promise对象。 然后取promise对象的返回值,作为await的结果。
async function fn () {
const res = await '答案cp3'
const res1 = await new Promise((resolve) => {
resolve('aaa')
})
console.log(res, res1)
}
fn() // 答案cp3 aaa
注意await只能用在async函数里,用在普通函数会报错。
function fn() {
await fn1() // errror: await is only valid in async functions and the top level bodies of modules
}
返回结果
因为async函数执行返回一个promise对象,promise的方法都适用,所以它可以通过then方法拿到对应返回结果。也可以通过await赋值给一个变量,拿到async函数的返回结果。
async function fn () {
return '答案cp3'
}
fn().then((res) => {
console.log(res) // 答案cp3
})
const res = await fn()
console.log(res) // 答案cp3
捕获错误
await是通过try-catch函数或者catch方法来捕获错误。
async function fn () {
return Promise.reject('error')
}
// 第一种方式
try{
await fn()
} catch(e) {
console.log(e)
}
// 第二种方式
fn().catch((e) => {
console.log(e)
})
// 类似
await fn().catch((e) => {console.log(e)})
如果没有捕获错误,一旦发生错误则后面的代码会中止。
async function fn () {
await Promise.reject('error')
console.log('go on') // 不会执行
}
fn()
async function fn () {
try{
await Promise.reject('error')
} catch(e) {}
console.log('go on') // 会执行
}
fn()
所以我们建议对于await都要使用try-catch或者catch方法。
await一般是用于后面函数需要依赖前面函数先执行,有依赖关系的,如果没有依赖关系的,不建议使用await。因为会导致函数执行耗时。
补充一点:
如果需要对数组进行遍历await,使用forEach是没有效果,最好是使用for,或者reduce。
async function fn(ms = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1')
resolve()
}, ms)
})
}
// 1s后同时打印1
[1,2,3].forEach(async() => {
await fn()
})
// 每隔1s打印1
for(let i in [1,2,3]) {
await fn()
}
// 每隔1s打印1
[1,2,3].reduce(async (acc,cur) => {
await acc
return await fn()
},[])