es6之async函数

含义

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

写法

async函数就是将Generator函数的星号(*)替换成async,将yield替换成await
依次读取两个文件,Generator函数写法:

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);
		}
	});
}

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函数对Generator函数的改进

  1. 内置执行器
    Generator函数的执行必须依靠执行器(例如co模块),而async函数自带执行器,即async函数的执行,和普通函数一样,只需要一行代码。

    asyncReadFile();
    
  2. 更好的语义
    asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

  3. 更广的适用性
    co模块约定:yield命令后面只能是Thunk函数或Promise对象。
    async函数的await:后面可以是Promise对象原始类型的值(数值、字符串和布尔值,但这是会自动转成立即resolved的Promise对象)。

  4. 返回值是Promise
    Generator函数返回值是Iterator对象,async函数返回的是Promise对象,可以用then方法指定下一步的操作。
    async函数可以看作是多个异步操作包装成的一个Promise对象,而await命令就是内部then命令的语法糖。

基本用法

async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回(就是直接跳出整个函数了),等到异步操作完成,再接着执行函数体内后面的语句。
async函数有多种使用形式。

async函数返回的是Promise对象,可以作为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 () => {};

语法

async函数的难点是错误处理机制。

返回Promise对象

async函数返回一个Promise对象,函数内部return语句返回的值,会成为then方法回调函数的参数。
async函数内部抛出错误,会导致返回的Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

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返回:如果await命令后面是一个Promise对象(或thenable对象),返回该对象的结果,如果不是,就直接返回对应的值。

返回reject状态await命令后面的Promise对象如果变成reject状态,则reject的参数会被catch方法的回调函数接收到。任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。

处理reject状态:即使某一异步操作失败,也不中断后续异步操作。1.将可能失败的异步操作放到try...catch结构里面。2.在可能失败的异步操作后再加一个catch方法,处理可能出现的错误。

错误处理

如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject
处理方法:将可能出错的await命令放入try...catch结构中,可以放多个await命令。

使用注意点

  1. 最好把await命令放在try...catch代码块中(或后加catch方法),防止await变成reject状态中断程序。
  2. 多个await命令,如果不存在继发关系,最好让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
  1. await命令只能用在async函数中,用在普通函数中会报错。
  2. async函数可以保留运行堆栈。
const a = async () => {
  await b();
  c();
};

上面代码中,b()运行的时候,a()是暂停执行,上下文环境都保存着。一旦b()或()报错,错误堆栈讲包括a()。

async函数的实现原理

async函数的实现原理,就是将Generator函数和自动执行器,包装在一个函数里。

你可能感兴趣的:(es6学习)