async/await 和 Generators + co 的写法非常的相似,只是把用于声明 Generator 函数的 * 关键字替换成了 async 并写在了 function 关键字的前面,把 yield 关键字替换成了 await;另外,async 函数是基于 Promise 的,await 关键字后面等待的异步操作必须是一个 Promise 实例,当然也可以是原始类型的值,只不过这时的执行效果等同于同步,与 Generator 不同的是,await 关键字前可以使用变量去接收这个正在等待的 Promise 实例执行后的结果。
async 函数的基本用法
async 函数返回一个 Promise 实例,可以使用 then 方法添加回调函数。当函数执行的时候,只要遇到 await 就会等待,直到 await 后面的同步或异步操作完成,再接着执行函数体内后面的语句。
ES6的声明
// 1- 函数声明
function * fn() {}
// 2- 函数表达式
const * fn = function() {};
ES7的声明
// 1- 函数声明
async function fn() {}
// 2- 函数表达式
const fn = async function() {};
// 3- 箭头函数
const fn = async () => {};
// 4- 作为对象的方法
let obj = {
async fn() {}
};
// 5- 作为 class 的方法
class Person(name) {
constructor () {
this.name = name;
}
async getName() {
const name = await this.name;
return name;
}
}
使用 NodeJS 的 fs 模块连续异步读文件,第一个文件名为 a.txt,读到的内容为 b.txt,作为要读的第二个文件的文件名,继续读 b.txt 后将读到的内容 “Hello world” 打印出来。
我们来使用 async/await 的方式来实现一下:
// 引入依赖
let fs = require("fs");
let util = require("util");
// 将 fs.readFile 转换成 Promise
let readFile = util.promisify(fs.readFile);
// 声明 async 函数
async function read(file) {
let aData = await readFile(file, "utf8");
let bData = await readFile(aData, "utf8");
return bData;
}
// 调用 async 函数
read("a.txt").then(data => {
console.log(data); // Hello world
});
与 Generator 函数一样,写法像同步,执行是异步,不同的是我们即没有手动调用 next 方法,也没有借助 co 库,其实是 async 函数内部集成了类似于 co 的执行器,帮我们在异步完成后自动向下执行代码,所以说 async/await 是 Generators + co 的语法糖。
await 异步并发
在 async 函数中,如果有多个 await 互不依赖,这种情况下如果执行一个,等待一个完成,再执行一个,再等待完成,这样是很浪费性能的,所以我们要把这些异步操作同时触发。
假设我们异步读取两个文件,且这两个文件不相关,我可以使用下面的方式来实现:
// 前置
let fs = require("fs");
let util = require("util");
let readFile = util.promisify(fs.readFile);
// 方法1- 需要改进的 async 函数
async function fn() {
let aData = await readFile("a.txt", "utf8");
let bData = await readFile("b.txt", "utf8");
return [aData, bData];
}
fn();
// 方法2- 在 async 函数外部触发异步
let aDataPromise = readFile("a.txt", "utf8");
let bDataPromise = readFile("b.txt", "utf8");
async function fn() {
let aData = await aDataPromise;
let bData = await bDataPromise;
return [aData, bData];
}
fn();
// 方法3- 使用 Promise.all
async function fn() {
let dataArr = await Promise.all(
readFile("a.txt", "utf8"),
readFile("a.txt", "utf8")
);
return dataArr;
}
fn();
异步并发实例
// 创建 Promise 实例
let p1 = Promise.resolve("p1 success");
let p2 = Promise.resolve("p2 success");
let p3 = Promise.resolve("p3 success");
// 1-错误的处理方式 async 函数
async function fn(promises) {
promise.forEach(function (promise) {
await promise;
});
}
fn([p1, p2, p3]); // 执行时报错
// 2-正确的打开方式 修改方式
async function fn(promises) {
for(let i = 0; i < promises.length; i++) {
await promises[i];
}
}
fn([p1, p2, p3]); // 正常执行