await
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
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 function foo() {
console.log(1);
await (() => { throw 3; })();
}
// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log);
console.log(2);
// 1
// 2
// 3单独的 Promise.reject()不会被异步函数捕获,而会抛出未捕获错误。不 过,对拒绝的期约使用 await 则会释放(unwrap)错误值(将拒绝 Promise 返回):
async function foo() {
console.log(1);
await Promise.reject(3);
console.log(4); // 这行代码不会执行
}
// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log);
console.log(2);
// 1
// 2
// 3异步策略
实现 sleep
async function sleep(delay){
return new Promise((resolve)=> setTimeout(resolve, delay))
}利用平行执行
正常串行写法,在不需要保证顺序的情况下属于需要优化的地方
async function randomDelay (id) {
// 延迟 0~1000 毫秒
const delay = Math.random() * 1000;
return new Promise((resolve) => setTimeout(() => {
console.log(`${id} finished`);
resolve();
}, delay));
}
async function foo () {
const t0 = Date.now();
await randomDelay(0);
await randomDelay(1);
await randomDelay(2);
await randomDelay(3);
await randomDelay(4);
console.log(`${Date.now() - t0}ms elapsed`);
}
foo();
// 0 finished
// 1 finished
// 2 finished
// 3 finished
// 4 finished
// 1570ms elapsedfor 循环写法
async function foo () {
const t0 = Date.now();
for (let i = 0; i < 5; ++i) {
await randomDelay(i);
}
console.log(`${Date.now() - t0}ms elapsed`);
}
foo();
// 0 finished
// 1 finished
// 2 finished
// 3 finished
// 4 finished
// 2494ms elapsed就算这些期约之间没有依赖,异步函数也会依次暂停,等待每个超时完成。这样可以保证执行顺序, 但总执行时间会变长。
如果顺序不是必需保证的,那么可以先一次性初始化所有期约,然后再分别等待它们的结果。
async function foo () {
const t0 = Date.now();
const promises = Array(5).fill(null).map((_, i) => randomDelay(i));
for (const p of promises) {
await p;
}
console.log(`${Date.now() - t0}ms elapsed`);
}
foo();虽然 Promise 没有按照顺序执行,但 await 按顺序收到了每个 Promise 的值
function foo () {
const t0 = Date.now();
const promises = Array(5).fill(null).map((_, i) => randomDelay(i));
for (const p of promises) {
console.log(`awaited ${await p}`);
}
console.log(`${Date.now() - t0}ms elapsed`);
}
foo();
// 1 finished
// 3 finished
// 0 finished
// awaited 0
// awaited 1
// 2 finished
// awaited 2
// awaited 3
// 4 finished
// awaited 4
// 958ms elapsedasync 函数
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)内置执行器。
(2)更好的语义。
(3)更广的适用性。
(4)返回值是Promise。
async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数。
实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}spawn
function spawn(genF) {
return new Promise(function (resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
function (v) {
step(function () {
return gen.next(v);
});
},
function (e) {
step(function () {
return gen.throw(e);
});
}
);
}
step(function () {
return gen.next(undefined);
});
});
}Async 和 await 的优化
性能提升取决于以下三个因素:
- TurboFan,新的优化编译器 🎉
- Orinoco,新的垃圾回收器 🚛
- 一个 Node.js 8 的 bug 导致 await 跳过了一些微 tick(microticks) 🐛
bug 优化
const p = Promise.resolve();
(async () => {
await p;
console.log("after:await");
})();
p.then(() => console.log("tick:a")).then(() => console.log("tick:b"));通过判断 await 后的 是否是一个 Promise,如果是的话直接返回,可以节省一个 Promise,只封装必要的 promise。这个操作在值已经是 promose 的情况下可以省去一个额外的 promise 和两个微任务。
去除了 throwaway
