async/await的用法及其异常捕获

什么是async/await

我们一般用Promise解决回调地狱的问题,用Promise.then来获取成功状态,但当多个相互依赖的请求时,代码可能会变成这样:

1
2
3
4
p1.then(res => {
...
p2.then(res1 => )
})

显然不太好读。因此就有了async/await:

  • async函数返回一个Promise对象,可以使用promise对象的方法。可以看作多个异步操作,包装成的一个 Promise 对象
  • await后面如果接的是promise对象,当Promise对象状态变化的时候,就会得到返回值,可以看作then的语法糖

基本用法

async函数

  • async函数返回的是一个Promise对象,如果函数中有返回值。则通过Promise.resole()封装成Promise对象

    • 使用then方法添加回调函数获取return后面的值
    • 使用catch 来捕获错误信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    async function fn() {
    return "hello"
    }
    console.log(fn()); //Promise {<resolved>: "hello"} //返回的是一个Promise对象
    fn().then(res => {
    console.log(res) //"hellp"
    })

    //捕获错误信息
    async function fn() {
    let obj = {};
    obj.run()
    return "hello"
    }

    fn().then(res => {
    console.log(res)
    }).catch(err => {
    console.log(err)//TypeError: obj.run is not a function
    })

await()方法

  • 只能在async函数中使用;

  • 函数外面访问不到await及后面的表达式,如果需要在函数外部获取就需要在await前面加return。

  • await后面如果接的是promise对象,当Promise对象状态变化的时候,得到返回值,如果接的是其他类型就直接返回。

    • await命令就是内部then命令的语法糖。

    • 必须等待内部所有await命令后面的Promise对象执行完,才会发生状态改变,才会执行then方法指定的回调函数

    • 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

      1
      2
      3
      4
      5
      async function f() {
      await Promise.reject('出错了');
      await Promise.resolve('hello world'); // 不会执行
      }
      f().then(res=>{console.log(res)}).catch(err=>{console.log(err);}) //出错了
    • 如果希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      async function f() {
      try {
      await Promise.reject('出错了');
      } catch(er) {
      }
      return await Promise.resolve('hello world');
      }

      f().then(res => console.log(res))
      // hello world
  • async函数内部代码是同步执行的,执行时如果碰到await,就会先返回;等待await后面的表达式出结果之后再继续执行函数体内后面的语句,但是await不会阻塞async函数外的代码,await等待过程中函数外的代码正常向下同步执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     let p = new Promise((resolve) => {
    setTimeout(() => {
    resolve("promise")
    }, 2000)
    })


    console.log("async 开始")
    async function fn() {
    console.log(1);
    let a = await p;
    console.log(a);
    console.log(2);
    return 3;
    }

    fn().next(res=>{
    console.log(res)
    })
    console.log("async 结束")
    //async 开始
    //1
    //async 结束
    //(两秒后)promise
    //2
    //3

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function http({
type = "get",
url,
data = {}
}) {
return new Promise((resolve, reject) => {
$.ajax({
type,
url,
data,
success: function(response) {
resolve(response)
}
});
})
}

async function demo() {
let res1 = await http({
url: "http://106.13.114.114:5000/api/firstCategory"
})
let firstId = res1.list[0][2].firstId;
let res2 = await http({
url: "http://106.13.114.114:5000/api/secondCategory",
data: {
firstId
}
})
console.log(res2)
}

demo()

异常捕获

如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject

防止出错的方法就是我们将其放在try/catch代码块中。并且能够捕获异常。

1
2
3
4
5
6
7
8
async function fn(){
try{
let a = await Promise.reject('error')
}catch(error){
console.log(error)
}
}

异常捕获的封装

如果不想每次需要捕获异常的时候都写很多try/catch,也可以对异常捕获进行封装:

1
2
3
function to(promise){
return promise.then(res=>[null,res]).catch(err=>[err,undefined])
}
1
2
3
4
// 使用举例
let firstRequest = async (index = 0) => {
let [err, res] = await to(ajax())
}