今天再来学学async 函数

321 阅读3分钟

这是我参与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()
},[])

参考

async 函数