Day 26: 异步编程async/await
📅 日期: 2026年03月18日
🎯 学习目标: 深入理解 async/await,掌握异步代码的同步写法
⏱️ 预计用时: 2-3 小时
📂 分类: 10-现代JavaScript
🎯 学习目标
通过今天的学习,你将:
- 理解 async/await 语法:让异步代码看起来像同步代码
- 掌握错误处理:try/catch、异常传播
- 学会并行处理:Promise.all + async/await
- 实践最佳实践:避免常见陷阱、性能优化
- 应用实战场景:API 请求、文件操作、数据库查询
💡 核心概念
1. async 函数定义
// async 函数总是返回 Promise
async function fetchUserData(userId) {
const response = await fetch('/api/users/' + userId);
const user = await response.json();
return user;
}
// 等价于
function fetchUserData(userId) {
return fetch('/api/users/' + userId)
.then(response => response.json());
}
2. await 表达式
// await 只能在 async 函数中使用
async function example() {
// 暂停执行,等待 Promise 解决
const data = await fetchData();
console.log(data);
// await 后的代码会在 data 准备好后执行
console.log('继续执行');
}
3. 错误处理
// 使用 try/catch
async function handleError() {
try {
const data = await riskyOperation();
return { success: true, data };
} catch (error) {
console.error('错误:', error);
return { success: false, error: error.message };
}
}
🛠️ API 函数详解
1. async 函数
// async 函数声明
async function getData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// async 箭头函数
const getUser = async (id) => {
const response = await fetch('/api/users/' + id);
return await response.json();
};
// async 方法
const obj = {
async fetchData() {
return await fetch('/api/data');
}
};
2. await 表达式
// await 等待 Promise 解决
const result = await promise;
// 可以 await 任何 thenable 对象
const thenable = {
then: (resolve) => resolve('value')
};
const value = await thenable;
// await 的优先级低于运算符
const sum = await promise1 + await promise2; // 并行执行
3. 错误处理
// try/catch/finally
async function withFinally() {
try {
await resource.acquire();
await doWork();
} catch (error) {
console.error(error);
} finally {
await resource.release(); // 总是执行
}
}
// 捕获特定类型的错误
async function specificCatch() {
try {
await riskyOperation();
} catch (error) {
if (error instanceof NetworkError) {
console.log('网络错误');
} else if (error instanceof ValidationError) {
console.log('验证错误');
} else {
throw error; // 重新抛出
}
}
}
🎮 实战示例
示例 1:顺序执行异步操作
// 使用 async/await 顺序执行
async function getDashboardData() {
const user = await getUser(1);
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
return { user, posts, comments };
}
// 相比 then 链
function getDashboardDataOld() {
return getUser(1)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id));
}
示例 2:并行执行异步操作
// 并行执行多个独立的异步操作
async function fetchAllData() {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
return { users, posts, comments };
}
// 使用 for...of 循环
async function fetchAllItems(urls) {
const results = [];
for (const url of urls) {
const response = await fetch(url);
const data = await response.json();
results.push(data);
}
return results;
}
示例 3:超时控制
// Promise.race + async/await
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
controller.abort();
reject(new Error('请求超时'));
}, timeout);
});
try {
const response = await fetch(url, {
signal: controller.signal
});
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('请求超时');
}
throw error;
}
}
⚠️ 重要注意事项
1. async 函数总是返回 Promise
// 正确:可以利用返回值
const promise = async function() {
return 'value';
}; // 返回 Promise
// 错误:忘记 await
async function wrong() {
const result = getData(); // 必须加 await!
console.log(result); // 输出 Promise 对象
}
2. await 只能在 async 函数中使用
// 正确:在 async 函数中使用 await
async function correct() {
const data = await fetchData();
return data;
}
// 错误:在非 async 函数中使用 await
function wrong() {
const data = await fetchData(); // SyntaxError!
return data;
}
3. 避免 Promise 构造器反模式
// ❌ 错误:Promise 构造器反模式
async function wrong() {
return new Promise((resolve, reject) => {
const data = await fetchData();
resolve(data);
});
}
// ✅ 正确:直接 await
async function correct() {
const data = await fetchData();
return data;
}
4. 并行执行技巧
// 使用 Promise.all 并行
async function parallel() {
const [result1, result2] = await Promise.all([
operation1(),
operation2()
]);
}
// 使用 for...of 并行
async function parallelLoop(items) {
const promises = items.map(item => processItem(item));
const results = await Promise.all(promises);
return results;
}
🎓 实战应用场景
场景 1: API 请求
// 获取用户完整信息
async function getUserInfo(userId) {
try {
const [profile, posts, friends] = await Promise.all([
fetch('/api/users/' + userId + '/profile').then(r => r.json()),
fetch('/api/users/' + userId + '/posts').then(r => r.json()),
fetch('/api/users/' + userId + '/friends').then(r => r.json())
]);
return {
profile,
posts,
friends
};
} catch (error) {
console.error('获取用户信息失败:', error);
throw error;
}
}
场景 2: 文件操作
// 读取多个文件
async function loadConfigFiles() {
const [config1, config2, config3] = await Promise.all([
readFile('./config1.json').then(JSON.parse),
readFile('./config2.json').then(JSON.parse),
readFile('./config3.json').then(JSON.parse)
]);
return { config1, config2, config3 };
}
// 顺序处理文件
async function processFiles(files) {
for (const file of files) {
const data = await readFile(file);
await processFile(data);
await writeFile('processed/' + file, data);
}
}
场景 3: 数据库查询
// 连接池查询
async function getUserWithPosts(userId) {
const client = await pool.connect();
try {
const user = await client.query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
if (!user) {
throw new Error('用户不存在');
}
const posts = await client.query(
'SELECT * FROM posts WHERE user_id = $1 ORDER BY created_at DESC',
[userId]
);
return { ...user, posts };
} finally {
client.release();
}
}
✍️ 练习任务
基础练习(必做)
-
async/await 转换:
- [ ] 将 then 部分转换为 async/await
- [ ] 将回调函数转换为 async 函数
- [ ] 处理错误情况
-
错误处理:
- [ ] 使用 try/catch 捕获错误
- [ ] 区分不同类型的错误
- [ ] 实现重试逻辑
-
并行执行:
- [ ] 使用 Promise.all 并行请求
- [ ] 使用 for…of 并行处理数组
- [ ] 处理部分失败的情况
进阶练习(推荐)
-
高级模式:
- Promise.allSettled + async/await
- 实现批量操作(并行、串行混合)
- 实现并发控制(限制并发数)
-
性能优化:
- 避免不必要的 await
- 使用缓存减少请求
- 实现请求去重
-
工具函数:
// 延迟函数 const delay = ms => new Promise(resolve => setTimeout(resolve, ms) ); // 重试函数 async function retry(fn, maxRetries = 3, delay = 1000) { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { if (i === maxRetries - 1) throw error; await delay(delay * (i + 1)); } } } // 超时函数 async function withTimeout(promise, timeoutMs) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('超时')), timeoutMs) ); return Promise.race([promise, timeout]); }
实战挑战(选做)
-
实现并发控制:
- 限制同时最多 N 个并发请求
- 队列管理待处理任务
-
实现进度追踪:
- 报告处理进度
- 实现进度条显示
- 支持取消操作
-
实现缓存机制:
- 内存缓存
- LocalStorage 持久化
- 缓存失效策略
💡 常见问题 FAQ
Q1: async/await 和 Promise 有什么区别?
A:
- async/await 是 Promise 的语法糖
- async/await 让异步代码看起来像同步代码
- 底层仍然使用 Promise
- 可以混用 Promise 和 async/await
Q2: 如何处理循环中的 await?
A:
// 串行执行
for (const item of items) {
await processItem(item); // 等待每个完成
}
// 并行执行(限制并发)
const concurrency = 5;
for (let i = 0; i < items.length; i += concurrency) {
const batch = items.slice(i, i + concurrency);
await Promise.all(batch.map(processItem));
}
Q3: 如何避免阻塞事件循环?
A:
- 不要在循环中使用 await(除非是串行场景)
- 使用 Promise.all() 并行处理
- 分批处理大量数据
- 使用 setImmediate/setTimeout 让出控制权
Q4: 如何调试 async 函数?
A:
// 添加日志
async function debugExample() {
console.log('1. 开始');
const result1 = await step1();
console.log('2. step1 完成');
const result2 = await step2();
console.log('3. step2 完成');
console.log('结果:', { result1, result2 });
}
📚 拓拓阅读
官方文档
推荐资源
相关工具
🎉 总结
今天我们深入学习了 async/await 异步编程,涵盖了:
✅ async/await 语法:让异步代码像同步代码
✅ 错误处理:try/catch、异常传播
✅ 并行处理:.Promise.all + async/await
✅ 最佳实践:避免常见陷阱、性能优化
✅ 实战应用:API 请求、文件操作、数据库查询
async/await 是现代 JavaScript 异步编程的主流方式,它让异步代码更易读、更易维护。
下一节课预告: Day 27 将学习 模块化开发与构建工具,学习如何组织大型项目!
💪 加油!坚持就是胜利!