一、异步编程的核心挑战
scss
// 典型的回调地狱(Callback Hell)
getUser(userId, function(user) {
getOrders(user.id, function(orders) {
getProducts(orders[0].id, function(products) {
renderPage(user, orders, products); // 嵌套层次加深
});
});
});
问题:嵌套回调导致代码难以阅读和维护,错误处理分散("回调地狱")。
二、进化之路:从 Promise 到 Async/Await
1. Promise:异步处理的基石
typescript
javascript
复制
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("Data loaded"), 1000);
});
};
fetchData()
.then(data => {
console.log(data);
return processData(data); // 返回新Promise
})
.catch(err => console.error("Error:", err)); // 统一错误处理
优势:
- 链式调用取代嵌套
- 集中错误处理(
catch
) - 状态不可逆(pending/fulfilled/rejected)
2. Async/Await:同步语法的异步魔法
javascript
javascript
复制
async function loadAllData() {
try {
const user = await getUser(userId); // 等待异步操作
const orders = await getOrders(user.id);
const products = await getProducts(orders[0].id);
return { user, orders, products };
} catch (error) {
console.error("Failed loading:", error);
throw new Error("Data loading failed");
}
}
// 使用示例
loadAllData().then(result => renderPage(result));
核心优势:
- 同步代码的书写风格
try/catch
捕获同步和异步错误- 与 Promise 100% 兼容(async 函数始终返回 Promise)
三、高级场景处理方案
1. 并行执行:加速异步任务
javascript
javascript
复制
// 使用 Promise.all 并行处理
async function fetchMultipleResources() {
const [users, products, config] = await Promise.all([
fetch('/api/users'),
fetch('/api/products'),
fetch('/config.json')
]);
return { users, products, config };
}
2. 竞态控制:最快响应优先
javascript
javascript
复制
// 使用 Promise.race 实现超时控制
async function fetchWithTimeout(url, timeout = 3000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Request timeout")), timeout)
);
return await Promise.race([fetchPromise, timeoutPromise]);
}
3. 批量容错处理
ini
javascript
复制
// 使用 Promise.allSettled 忽略个别失败
const promises = [queryAPI('A'), queryAPI('B'), queryAPI('C')];
const results = await Promise.allSettled(promises);
const successfulData = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
四、十大最佳实践与避坑指南
- 始终返回 Promise:Async 函数自动包装返回值
csharp
javascript
复制
// ✅ 正确
async function getUser() { return db.query(...); }
// ❌ 危险
async function getUser() { db.query(...); } // 返回 undefined!
- 避免阻塞性等待:
scss
javascript
复制
// ✅ 并行优化
const [a, b] = await Promise.all([taskA(), taskB()]);
// ❌ 顺序阻塞
const a = await taskA(); // 需等待完成
const b = await taskB(); // 才开始执行
- 循环中的异步陷阱:
javascript
javascript
复制
// ❌ 错误:forEach 内的 async 无法等待
array.forEach(async item => await process(item));
// ✅ 正确:使用 for...of 顺序执行
for (const item of array) await process(item);
// ✅ 并行:使用 map + Promise.all
await Promise.all(array.map(item => process(item)));
- 必做错误捕获:
ini
javascript
复制
// 顶级捕获方案 (Node.js)
process.on('unhandledRejection', err => logger.fatal(err));
// 浏览器环境
window.addEventListener('unhandledrejection', e => {
e.preventDefault();
reportError(e.reason);
});
- 取消异步支持:
scss
javascript
复制
// 使用 AbortController
const controller = new AbortController();
fetch('/api', { signal: controller.signal })
.then(...)
.catch(e => if (e.name === 'AbortError') ...);
// 取消请求
controller.abort();
五、未来展望:Top-level await 与 Async Context
- Top-level await:ES2022 支持在模块顶层使用 await
arduino
javascript
复制
// module.js
const config = await fetchConfig();
export default config;
- Async Context API:提案中用于追踪异步调用链
结语
JavaScript 异步处理已从回调地狱演进到如今优雅的 async/await 模式。核心建议:
- 基础场景:Async/Await + try/catch
- 复杂流程:Promise.all/race/allSettled
- 生产环境:必须添加全局未处理拒绝捕获
掌握这些方案,你将能写出健壮、可读且高性能的异步 JavaScript 代码!
示例代码测试环境:Node.js 16+ / Chrome 100+
工具推荐:ESLint(强制 async 错误检查)、Async Hooks(Node 异步追踪)