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 函数和自动执行器
- 提供了更直观的错误处理机制
- 在实际开发中要注意避免常见陷阱,合理使用并行执行
- 性能优化需要考虑并行操作和超时处理