async/await

541 阅读2分钟

async

async/await实际上是Generator的语法糖。顾名思义,async关键字代表后面的函数中有异步操作,await表示等待一个异步方法执行完成。声明异步函数只需在普通函数前面加一个关键字async即可,如:

async function funcA() {}

async 函数返回一个Promise对象(如果指定的返回值不是Promise对象,也返回一个Promise,只不过立即 resolve ,处理方式同 then 方法),因此 async 函数通过 return 返回的值,会成为 then 方法中回调函数的参数:

async function funcA() {
  return 'hello!';
}

funcA().then(value => {
  console.log(value);
})
// hello!

单独一个 async 函数,其实与Promise执行的功能是一样的,接下里让我们看看 await 都干了些啥。

await

顾名思义, await 就是异步等待,它等待的是一个Promise,因此 await 后面应该写一个Promise对象,如果不是Promise对象,那么会被转成一个立即 resolve 的Promise。 async 函数被调用后就立即执行,但是一旦遇到 await 就会先返回,等到异步操作执行完成,再接着执行函数体内后面的语句。总结一下就是:async函数调用不会造成代码的阻塞,但是await会引起async函数内部代码的阻塞。看看下面这个例子:

async function func() {
  console.log('async function is running!');
  const num1 = await 200;
  console.log(`num1 is ${num1}`);
  const num2 = await num1+ 100;
  console.log(`num2 is ${num2}`);
  const num3 = await num2 + 100;
  console.log(`num3 is ${num3}`);
}

func();
console.log('run me before await!');
// async function is running!
// run me before await!
// num1 is 200
// num2 is 300
// num3 is 400

可以看出调用 async func 函数后,它会立即执行,首先输出了'async function is running!',接着遇到了 await 异步等待,函数返回,先执行func()后面的同步任务,同步任务执行完后,接着await等待的位置继续往下执行。可以说,async函数完全可以看作多个异步操作,包装成的一个Promise 对象,而await命令就是内部then命令的语法糖。

值得注意的是, await 后面的 Promise 对象不总是返回 resolved 状态,只要一个 await 后面的Promise状态变为 rejected ,整个 async 函数都会中断执行,为了保存错误的位置和错误信息,我们需要用 try...catch 语句来封装多个 await 过程,如下:

async function func() {
  try {
    const num1 = await 200;
    console.log(`num1 is ${num1}`);
    const num2 = await Promise.reject('num2 is wrong!');
    console.log(`num2 is ${num2}`);
    const num3 = await num2 + 100;
    console.log(`num3 is ${num3}`);
  } catch (error) {
    console.log(error);
  }
}

func();
// num1 is 200
// 出错了
// num2 is wrong!

如上所示,在 num2 await 得到了一个状态为 rejected 的Promise对象,该错误会被传递到 catch 语句中,这样我们就可以定位错误发生的位置。

async/await比Promise强在哪儿?

接下来我们用async/await改写一下Promise章节中关于sayHi的一个例子,代码如下:

function sayHi(name) {
  return new Promise((resolved, rejected) => {
    setTimeout(() => {
      resolved(name);
    }, 2000)
  })
}

async function sayHi_async(name) {
  const sayHi_1 = await sayHi(name)
  console.log(`你好, ${sayHi_1}`)
  const sayHi_2 = await sayHi('李四')
  console.log(`你好, ${sayHi_2}`)
  const sayHi_3 = await sayHi('王二麻子')
  console.log(`你好, ${sayHi_3}`)
}

sayHi_async('张三')
// 你好, 张三
// 你好, 李四
// 你好, 王二麻子

与之前长长的then链和then方法里的回调函数相比,这样的写法看起来像是同步写法并且更加清爽,更加符合编程习惯。