async/await 原理解析
理解 async/await 基于 Generator 和自动执行器的实现机制
问题
async/await 是如何实现的?它与 Generator 有什么关系?
解答
async/await 本质上是 Generator 函数的语法糖,配合自动执行器实现异步代码的同步写法。
Generator 基础
function* gen() {
const a = yield Promise.resolve(1);
const b = yield Promise.resolve(2);
return a + b;
}
const g = gen();
console.log(g.next()); // { value: Promise{1}, done: false }
console.log(g.next(1)); // { value: Promise{2}, done: false }
console.log(g.next(2)); // { value: 3, done: true }
Generator 可以暂停执行,但需要手动调用 next() 并传入上一次的结果。
自动执行器
function co(generatorFn) {
return function (...args) {
// 获取迭代器
const gen = generatorFn.apply(this, args);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = gen[key](arg);
} catch (error) {
return reject(error);
}
const { value, done } = result;
if (done) {
// 迭代完成,返回最终值
resolve(value);
} else {
// 继续迭代,等待 Promise 完成后递归调用
Promise.resolve(value).then(
(val) => step('next', val),
(err) => step('throw', err)
);
}
}
// 启动迭代
step('next');
});
};
}
使用示例
// 模拟异步请求
function fetchData(id) {
return new Promise((resolve) => {
setTimeout(() => resolve(`data-${id}`), 100);
});
}
// 使用 Generator + co
const getData = co(function* () {
const a = yield fetchData(1);
const b = yield fetchData(2);
return [a, b];
});
getData().then(console.log); // ['data-1', 'data-2']
// 等价的 async/await 写法
async function getDataAsync() {
const a = await fetchData(1);
const b = await fetchData(2);
return [a, b];
}
getDataAsync().then(console.log); // ['data-1', 'data-2']
错误处理
const withError = co(function* () {
try {
const a = yield Promise.reject(new Error('failed'));
} catch (e) {
console.log('caught:', e.message);
return 'fallback';
}
});
withError().then(console.log); // caught: failed → fallback
对比转换
| async/await | Generator + co |
|---|---|
async function | co(function*) |
await promise | yield promise |
return value | return value |
try/catch | try/catch |
关键点
async函数返回 Promise,await暂停执行等待 Promise 完成- Generator 的
yield可以暂停函数,next()恢复执行并传值 - 自动执行器递归调用
next(),将 Promise 结果传回 Generator await后的值会被Promise.resolve()包装,非 Promise 也能用- 错误通过
gen.throw()抛入 Generator,可被try/catch捕获
目录