JavaScript中的异步编程解决方案一直在发展,从ES5的Callback到ES6的PromiseGenerator,再到ES7的async/await。对于开发人员越来越友好、直观,写法越来越符合人们的直觉。

async/await

Node.js 7.6以上版本才支持async/await。

async/await的目的是在Promise的基础上进一步简化异步的同步调用。

调用async函数时会返回一个Promise对象。当这个async函数返回一个值时,Promise的resolve方法会负责传递这个值;当async函数抛出异常时,Promise的reject方法也会传递这个异常值。

await“等待”的是一个Promise对象或其他任意value。如果await后面跟的不是一个Promise,它被转化为Promise.resolve(value)。

await只能在async内部使用。

例子

例子1

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

function add1(x) {
  var a = await resolveAfter2Seconds(20);
  return a;
}

/* 因为await没有在async内部使用,运行时会报错:
   var a = await resolveAfter2Seconds(20);
                 ^^^^^^^^^^^^^^^^^^^^
   SyntaxError: Unexpected identifier
 */

例子2

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function add1(x) {
  var a = resolveAfter2Seconds(20);
  var b = resolveAfter2Seconds(30);
  return x + await a + await b;
}

add1(10).then(v => {
  console.log(v);  // 2秒后打印60
});

async function add2(x) {
  var a = await resolveAfter2Seconds(20);
  var b = await resolveAfter2Seconds(30);
  return x + a + b;
}

add2(10).then(v => {
  console.log(v);  // 4秒后打印60
});

/* 
   注意以上打印结果的时间,await的串行和并行执行,await的位置不同,执行的顺序也不一样。
 */

异常处理

用try catch捕获async/await的异常:

try {
  await f();
}
catch (err) {
  // 静态函数Promise.reject会走到这里
  // async代码异常,也会走这里
}

最佳实践

将基本的业务逻辑封装为一个个Promise,然后使用async/await对Promises进行组合调用。

async/await的与Generator的联系与区别

async函数就是Generator函数的语法糖。

async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,仅此而已。

如何在for循环中使用async/await

使用for of代替forEach。

参考:https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop

参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await