async/await

介绍

ES6出现promise之后,再也不用担心回调地狱,但是promise也不是十全十美,async/await可以让异步代码看起来就像同步的一样,这就是要学习它的原因。

首先创建一个简单的 promise:

function getData() {
  return new Promise(function(resolve) {
    let data = { name: "Tom" };
    resolve(data);
  });
}
let p = getData();
p.then(data => {
  console.log(data);
  return "over";
});

getData返回一个新的promise实例,成功resolve之后返回data的值,上面的写法是用ES6promise的写法。

async function go() {
  console.log(await getData());
  return "over";
}
console.log(go);
go();

async用于定义一个异步函数,该函数隐式的返回一个Promise,该promisereosolve值就是函数return的值。(示例中reosolve值就是字符串over)。

await必须在async定义的函数中使用,当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

可以看出用async/await写出来的代码,最明显的一点就是代码要比promise简洁。

第二个优势,async/await可以让try/catch既能处理同步错误也可以处理异步产生的错误,在一般情况下使用try/catch是不能够检测异步错误的,因为try/catch本身就是同步的,这也就是为什么说async/await看起来像同步的原因之一。

async function go() {
  try{
    console.log(JSON.parse(await getData())
    return 'over'
  } catch(error) {
    cons
  }
}

有时候可能需要嵌套异步,也就是这样:

function go() {
  return promise1().then(value1 => {
    return promise2(value1).then(value2 => {
      return promise3(value1, value2);
    });
  });
}

循环嵌套看起来很让人不爽,一个解决的方法是使用promise.all

function go() {
  return promise1()
    .then(value1 => {
      return promise.all([value1, promise2(value1)]);
    })
    .then((value1, value2) => {
      return promise3(value1, value2);
    });
}

但这样做可读性未免也太差了,而且将promise.all本身的作用是用来将多个Promise实例包装起来,这样直接和value1一起使用也太奇怪了。

最好的方法是使用async/await

async function go() {
  const value1 = await promise1();
  const value2 = await promise2(value1);
  return promise3(value1, value2);
}

async/await可以让异步代码变得“同步”,但是会使异步代码不再明显,识别异步代码可能要花一些功夫。

深入理解

async函数的返回值是一个promise对象:

async function test() {
  return "hello";
}
const a = test();
console.log(a);

返回值出乎意料,是一个promise对象:

Promise {<resolved>: "hello"}

可以得出一个结论:async函数返回的是一个promise对象,如果在函数中return一个直接量,async会把这个值通过promise.resolve()封装成promise对象,如果没有返回值输出的则是Promise.resolve(undefined)

我们知道await不能在async函数外使用,所以当无法使用await获取返回值的时候还是要用then

test().then(v => {
  console.log(v);
});

如果没有await,那么async函数会立即执行,并返回一个promise对象,这和一个返回promise对象的普通函数没有区别。

await等待的是一个表达式,计算结果是promise或者其他值,await等待的实际上是一个返回值,它可以用于任意表达式的返回结果,根据结果分两种情况:

  • 如果await最后等到的不是一个promise对象,那么await的运算结果就是表达式返回的东西。
  • 如果await最后等到的是一个promise对象,那么他会等待promise的状态变成resolve,然后得到resolve的值,作为await表达式的运算结果。

WARNING

await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。