async/await 原理与异步工具
async/await 的本质
async/await 是 Generator + 自动执行器(co) 的语法糖:
async function≈ 返回 Promise 的 Generator 函数await≈yield,暂停函数执行,等待 Promise 完成后把结果回填
用 Generator + co 模拟 async/await
// 自动执行器:不断驱动 generator,遇到 Promise 就等它完成再继续
function co(genFn) {
return new Promise((resolve, reject) => {
const gen = genFn();
function step(nextFn) {
let next;
try {
next = nextFn(); // 可能是 gen.next(v) 或 gen.throw(e)
} catch (e) {
return reject(e); // generator 内部抛错
}
const { value, done } = next;
if (done) return resolve(value); // 执行完毕
// 把 yield 出来的值统一包成 Promise
Promise.resolve(value).then(
(v) => step(() => gen.next(v)), // 成功:把结果回填给 yield
(e) => step(() => gen.throw(e)) // 失败:把错误抛回 generator
);
}
step(() => gen.next());
});
}
// 用法:把 async 换成 co,await 换成 yield
co(function* () {
const a = yield Promise.resolve(1);
const b = yield Promise.resolve(a + 1);
return a + b; // 3
}).then(console.log);
sleep(暂停)
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function demo() {
console.log("start");
await sleep(1000);
console.log("1s later");
}
失败重试 retry
function retry(fn, times = 3, delay = 1000) {
return new Promise((resolve, reject) => {
const attempt = (remaining) => {
Promise.resolve(fn())
.then(resolve)
.catch((err) => {
if (remaining <= 0) return reject(err);
setTimeout(() => attempt(remaining - 1), delay);
});
};
attempt(times);
});
}
常见追问
await后面跟非 Promise 会怎样? 会被Promise.resolve()包裹,下一行进入微任务队列。- async 函数一定异步吗? 函数体内第一个
await之前的代码是同步执行的,遇到await才让出。 - 如何并行多个 await? 用
Promise.all,避免「串行 await」造成的等待累加。