async...await...

48 阅读1分钟

什么是async函数?

  • async是异步方案的一种
  • async是Generator函数的语法糖

async与Generator的关系

  • 内置执行器:Generator函数的执行必须靠执行器,而async函数自带执行器。
  • 更简洁的语法:function*与yield被async和await替代。
  • 返回值是Promise:Generator的返回值是Iterator对象,而async函数的返回值是Promise,除了await,你还可以使用then方法来指定下一步操作。

分别用Generator函数和async函数来实现依次读取两个文件的操作。

const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};

Generator函数如下:

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

async函数:

const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

基本用法

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

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭头函数
const foo = async () => {};

语法

返回Promise对象

async函数返回一个Promise对象,返回的值会成为then方法回调函数的参数。

async function f() {
  throw new Error('出错了');
}

f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)
)
//reject Error: 出错了

Promise对象的状态变化

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

await命令

await命令的返回值有三种:

  • Promise对象(Promise、Promise.resolve()、thenable对象、被catch的Promise.reject())
  • 常量值
  • 报错问题(Promise.reject()、new Error)

错误处理

await报错的问题,可以通过以下两种方式捕获处理:

  • 将可能出现报错的代码放置在try...catch...块中。
async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了');
    });
  } catch(e) {
  }
  return await('hello world');
}
  • 通过后续的.then或.catch捕获错误。
async function f() {
  await new Promise(function (resolve, reject) {
    throw new Error('出错了');
  });
}

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

await支持顶层使用

注意,顶层await只能用在 ES6 模块,不能用在 CommonJS 模块。这是因为 CommonJS 模块的require()是同步加载,如果有顶层await,就没法处理加载了。

使用场景如下:

// import() 方法加载
const strings = await import(`/i18n/${navigator.language}`);

// 数据库操作
const connection = await dbConnector();

// 依赖回滚
let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}
// x.js
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");

// y.js
console.log("Y");

// z.js
import "./x.js";
import "./y.js";
console.log("Z");