实现async/await
手写实现 async/await 语法糖,理解异步编程的本质
问题
async/await 是 ES2017 引入的异步编程语法糖,它基于 Generator 函数和 Promise 实现。本题要求手写实现 async/await 的机制,理解其背后的执行原理。
async/await 的本质是:
- async 函数返回一个 Promise
- await 会暂停函数执行,等待 Promise 完成
- 通过 Generator 函数的 yield 和迭代器实现暂停和恢复
解答
/**
* 实现 async/await 的函数
* @param {GeneratorFunction} generatorFunc - Generator 函数
* @returns {Promise} 返回一个 Promise
*/
function asyncToGenerator(generatorFunc) {
return function(...args) {
// 创建 Generator 对象
const generator = generatorFunc.apply(this, args);
return new Promise((resolve, reject) => {
/**
* 递归执行 Generator
* @param {string} key - 'next' 或 'throw'
* @param {any} arg - 传递给 Generator 的参数
*/
function step(key, arg) {
let result;
try {
// 执行 Generator 的 next 或 throw 方法
result = generator[key](arg);
} catch (error) {
// 如果执行出错,reject Promise
return reject(error);
}
const { value, done } = result;
if (done) {
// Generator 执行完毕,resolve 最终结果
return resolve(value);
} else {
// 将 value 包装成 Promise,继续递归执行
return Promise.resolve(value).then(
val => step('next', val), // 成功时继续执行
err => step('throw', err) // 失败时抛出错误
);
}
}
// 开始执行
step('next');
});
};
}
使用示例
// 示例1:基本使用
function* fetchUser() {
console.log('开始获取用户信息');
const user = yield Promise.resolve({ id: 1, name: 'Alice' });
console.log('用户信息:', user);
const orders = yield Promise.resolve(['订单1', '订单2']);
console.log('订单列表:', orders);
return { user, orders };
}
const asyncFetchUser = asyncToGenerator(fetchUser);
asyncFetchUser().then(result => {
console.log('最终结果:', result);
});
// 输出:
// 开始获取用户信息
// 用户信息: { id: 1, name: 'Alice' }
// 订单列表: ['订单1', '订单2']
// 最终结果: { user: { id: 1, name: 'Alice' }, orders: ['订单1', '订单2'] }
// 示例2:错误处理
function* fetchWithError() {
try {
const data = yield Promise.reject(new Error('网络错误'));
console.log('这行不会执行');
} catch (error) {
console.log('捕获到错误:', error.message);
return '错误已处理';
}
}
const asyncFetchWithError = asyncToGenerator(fetchWithError);
asyncFetchWithError().then(result => {
console.log('结果:', result);
});
// 输出:
// 捕获到错误: 网络错误
// 结果: 错误已处理
// 示例3:模拟真实的 async/await
function* getData() {
const response1 = yield fetch('https://api.example.com/data1');
const data1 = yield response1.json();
const response2 = yield fetch(`https://api.example.com/data2?id=${data1.id}`);
const data2 = yield response2.json();
return { data1, data2 };
}
const asyncGetData = asyncToGenerator(getData);
// 等价于:
// async function getData() {
// const response1 = await fetch('https://api.example.com/data1');
// const data1 = await response1.json();
//
// const response2 = await fetch(`https://api.example.com/data2?id=${data1.id}`);
// const data2 = await response2.json();
//
// return { data1, data2 };
// }
// 示例4:传递参数
function* greet(name) {
const greeting = yield Promise.resolve(`Hello, ${name}!`);
const farewell = yield Promise.resolve(`Goodbye, ${name}!`);
return `${greeting} ${farewell}`;
}
const asyncGreet = asyncToGenerator(greet);
asyncGreet('Bob').then(result => {
console.log(result); // Hello, Bob! Goodbye, Bob!
});
关键点
-
Generator 函数:使用 Generator 函数的
yield关键字实现函数执行的暂停和恢复 -
Promise 包装:将每个
yield的值用Promise.resolve()包装,统一处理同步和异步值 -
递归执行:通过递归调用
step函数,不断执行 Generator 的next()方法,直到done为true -
错误处理:使用
try-catch捕获同步错误,使用 Promise 的catch处理异步错误,通过generator.throw()将错误传回 Generator -
返回 Promise:整个函数返回一个 Promise,使其可以被
.then()或其他await调用 -
上下文绑定:使用
apply(this, args)保持函数执行时的this上下文和参数传递 -
done 判断:当 Generator 执行完毕(
done === true)时,resolve 最终的返回值
目录