JavaScript 中 async/await 的工作原理
1. 基本概念
async/await 是 JavaScript 中处理异步操作的语法糖,它建立在 Promise 之上,让异步代码看起来更像同步代码。
async
函数总是返回一个 Promiseawait
关键字只能在async
函数内部使用await
会暂停当前async
函数的执行,等待 Promise 解决
1.1 实现机制
async
函数会自动将返回值封装为Promise
对象await
会暂停当前async
函数的执行,直至Promise
状态变为resolved
- 当
Promise
被成功解析后,await
表达式会返回其解析值 - 若
Promise
被拒绝,await
会抛出拒绝原因,可通过try/catch
进行异常捕获
2. async/await 的工作原理
2.1 Generator 函数的基础
async/await 的实现原理与 Generator 函数密切相关。实际上,async/await 是对 Generator 的封装,让异步操作的写法更加优雅。
// Generator 函数写法
// 模拟异步请求函数
function fetchData1() {return new Promise((resolve) => {setTimeout(() => {resolve({ id: 1, name: 'John' });}, 1000);});
}function fetchData2(data) {return new Promise((resolve) => {setTimeout(() => {resolve({...data,age: 30,email: 'john@example.com'});}, 1000);});
}// Generator 函数写法
function* gen() {console.log('开始获取用户基本信息...');const data1 = yield fetchData1();console.log('获取到的基本信息:', data1);console.log('开始获取用户详细信息...');const data2 = yield fetchData2(data1);console.log('获取到的详细信息:', data2);return data2;
}// 手动执行 Generator
function runGenerator(generator) {const iter = generator();function run(data) {const result = iter.next(data);if (result.done) {return result.value;}return result.value.then(data => run(data));}return run();
}// 使用 Generator
console.log('使用 Generator 方式获取数据:');
runGenerator(gen).then(finalData => {console.log('Generator 最终结果:', finalData);
});// async/await 写法
async function fetchData() {console.log('开始获取用户基本信息...');const data1 = await fetchData1();console.log('获取到的基本信息:', data1);console.log('开始获取用户详细信息...');const data2 = await fetchData2(data1);console.log('获取到的详细信息:', data2);return data2;
}// 使用 async/await
console.log('使用 async/await 方式获取数据:');
fetchData().then(finalData => {console.log('async/await 最终结果:', finalData);
});
2.2 内部转换机制
当我们使用 async/await 时,JavaScript 引擎会将其转换为 Promise 链。例如:
// 原始的 async/await 代码
async function example() {const result1 = await asyncOperation1();const result2 = await asyncOperation2(result1);console.log(result2);return result2;
}// 转换后的 Promise 链(理解原理用)
function example() {return new Promise((resolve, reject) => {// 开始第一个异步操作asyncOperation1().then(result1 => {// 使用第一个操作的结果开始第二个异步操作return asyncOperation2(result1);}).then(result2 => {// 打印结果console.log(result2);// 返回最终结果resolve(result2);}).catch(error => {// 处理过程中的任何错误reject(error);});});
}
3. 实际应用示例
3.1 基本使用示例
// 模拟异步操作
function delay(ms) {return new Promise(resolve => setTimeout(resolve, ms));
}async function fetchUserData(userId) {try {console.log('开始获取用户数据...');// 模拟API请求await delay(1000);const user = { id: userId, name: 'John Doe' };// 模拟获取用户订单await delay(500);const orders = [{ id: 1, product: 'Book' }];return { user, orders };} catch (error) {console.error('获取数据失败:', error);throw error;}
}// 使用方式
fetchUserData(1).then(data => console.log('获取的数据:', data)).catch(error => console.error('错误:', error));
3.2 并行执行示例
async function fetchMultipleData() {try {// 并行执行多个异步操作const [users, products, orders] = await Promise.all([fetchUsers(),fetchProducts(),fetchOrders()]);return { users, products, orders };} catch (error) {console.error('获取数据失败:', error);throw error;}
}
4. 错误处理机制
async/await 提供了更直观的错误处理方式,可以使用 try/catch 语句:
async function handleErrors() {try {const data = await riskyOperation();return data;} catch (error) {console.error('操作失败:', error);// 可以选择重试、返回默认值或抛出自定义错误throw new CustomError('操作失败,请稍后重试');} finally {// 清理工作await cleanup();}
}
5. 常见陷阱和最佳实践
5.1 循环中的 await
// 错误示例 - 串行执行
async function processItems(items) {for (const item of items) {await processItem(item); // 每次循环都要等待}
}// 正确示例 - 并行执行
async function processItems(items) {const promises = items.map(item => processItem(item));await Promise.all(promises);
}
5.2 错误传播
async function outerFunction() {try {await innerFunction();} catch (error) {// 处理所有内部 async 函数抛出的错误console.error('内部函数错误:', error);}
}async function innerFunction() {throw new Error('Something went wrong');
}
6. 总结
- async/await 是基于 Promise 的语法糖,使异步代码更易读和维护
- 它的实现原理基于 Generator 函数和自动执行器
- 提供了更直观的错误处理机制
- 在实际开发中要注意避免常见陷阱,合理使用并行执行
- 性能优化需要考虑并行操作和超时处理