8

async await

  • async await是异步编程的另一种解决方案

  • async函数是对Generator函数的改进

async的基本用法

async函数

async函数返回一个 Promise 实例,可以使用then方法(为返回的Promise实例)添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

  • 例 1:

clipboard.png

  • 上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise实例。

  • async函数内部return语句返回的值,会成为then方法回调函数的参数。

Promise对象的状态变化

async函数返回的 Promise对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

await命令的基本用法

正常情况下,await命令后面是一个Promise实例,如果不是,会被转成一个立即resolvePromise实例。

async function f() {
  return await 123;
}

f().then(v => console.log(v))

等价于

async function f() {
    return await new Promise(function(resolve){
        resolve(123)
    })
}
f().then(v => console.log(v))

等价于

async function f() {
    return await Promise.resolve('123')
}
f().then(v => console.log(v))
  • await语句的返回值是await命令后面Promise实例的结果(异步处理的结果)

function getResult() {
  return new Promise((resolve) => {
    resolve('result: 1000')  // resolve()方法的参数就是异步处理的结果
  });
}

async function asyncPrint() {
  const result = await getResult()  // 将异步处理的结果赋值给result
  return result
}

asyncPrint().then( (result) => { console.log(result) } ) //'result: 1000'

异常处理

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

async function f() {
  await new Promise(function (resolve, reject) {
    throw new Error('出错了');
  });
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:出错了

防止出错的方法,也是将其放在try...catch代码块之中。


async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了');
    });
  } catch(e) {
  }
  return await('hello world');
}

使用注意

  • 如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject,所以最好把await命令放在try...catch代码块中。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });
}
  • 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

let foo = await getFoo();
let bar = await getBar();

上面代码中,getFoogetBar是两个独立的异步操作(即互不依赖),被写成继发关系(只有执行完getFoo操作,才能去执行getBar操作)。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。

解释:这里的getFoogetBar方法会返回两个Promise实例(假设是发起Ajax请求,请求foo和bar的内容),只有执行了方法,对应的操作才会执行,如果写成上面的形式,就会导致执行完getFoo的操作后(等待收到服务器的响应后),才能执行getBar的操作,这样就成了同步,比较耗时,因此可以将上面的写法修改,使得在等待getFoo执行完的时间内(在等待服务器响应的期间)去执行执行getBar

记住:当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

上面两种写法,getFoogetBar都是同时触发,这样就会缩短程序的执行时间。

参考资料

ECMAScript 6 入门


jhhfft
590 声望40 粉丝

Write the Code. Change the World.