JavaScript 异步函数详解:从回调地狱到优雅控制流
1. 异步编程基础
为什么需要异步?
JavaScript 是单线程语言,异步操作(如网络请求、文件读写)可以避免阻塞主线程,提升性能和用户体验。
回调函数(Callback)
最早的异步模式,通过函数传递处理结果。
问题:嵌套过深会导致“回调地狱”(Callback Hell)。
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
fetchData((data) => {
console.log(data); // "Data received"(1秒后输出)
});
回调地狱示例
getUser(userId, (user) => {
getOrders(user.id, (orders) => {
getProducts(orders[0].id, (products) => {
console.log(products); // 多层嵌套,难以维护
});
});
});
2. Promise:更优雅的异步方案
Promise 的状态
- Pending:初始状态。
- Fulfilled:操作成功完成。
- Rejected:操作失败。
基本用法
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success!"); // 或 reject("Error!");
}, 1000);
});
promise
.then((result) => console.log(result)) // "Success!"
.catch((error) => console.error(error));
链式调用
解决回调地狱的关键!
fetch("/api/user")
.then((response) => response.json())
.then((user) => fetch(`/api/orders/${user.id}`))
.then((response) => response.json())
.catch((error) => console.error("Failed:", error));
Promise 工具方法
Promise.all()
:所有 Promise 成功时返回结果数组。Promise.race()
:第一个完成的 Promise 决定结果。
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
Promise.all([p1, p2]).then(([r1, r2]) => {
console.log(r1 + r2); // 3
});
3. Async/Await:同步风格的异步代码
基本语法
用 async
标记异步函数,await
等待 Promise 完成。
async function fetchUser() {
try {
const response = await fetch("/api/user");
const user = await response.json();
console.log(user);
} catch (error) {
console.error("Failed:", error);
}
}
错误处理
try/catch
捕获异常。- 或通过
.catch()
处理。
async function loadData() {
const data = await fetchData().catch((error) => {
console.error("Fallback data");
return defaultData;
});
console.log(data);
}
并行优化
避免不必要的串行等待:
// 错误:串行执行(耗时 2000ms)
const a = await fetchA(); // 1000ms
const b = await fetchB(); // 再1000ms
// 正确:并行执行(耗时 1000ms)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
4. 高级应用与性能优化
1. 取消异步操作
- AbortController:取消
fetch
请求。
const controller = new AbortController();
setTimeout(() => controller.abort(), 500); // 500ms后取消
fetch("/api/data", { signal: controller.signal })
.catch((err) => {
if (err.name === "AbortError") {
console.log("Request aborted");
}
});
2. 生成器 + Promise
用 function*
和 yield
实现更灵活的异步控制流。
function* asyncGenerator() {
const result = yield fetch("/api/data");
console.log(result);
}
const gen = asyncGenerator();
gen.next().value.then((data) => gen.next(data));
3. 异步迭代(for-await-of)
遍历异步数据流(如分页接口):
async function processAsyncData() {
const asyncIterable = getAsyncDataStream(); // 返回异步迭代器
for await (const data of asyncIterable) {
console.log(data);
}
}
5. 实战对比:回调 vs Promise vs Async/Await
场景 | 回调函数 | Promise | Async/Await |
---|---|---|---|
简单异步任务 | ✅ 简单 | ✅ 链式调用 | ✅ 代码直观 |
错误处理 | ❌ 多层嵌套难维护 | ✅ .catch() 统一处理 | ✅ try/catch 同步风格 |
并行任务 | ❌ 手动协调 | ✅ Promise.all | ✅ await Promise.all |
可读性 | ❌ 差 | ✅ 较好 | ✅ 最佳 |
总结
- 回调函数:基础但易导致嵌套地狱。
- Promise:通过链式调用解耦异步逻辑。
- Async/Await:以同步写法写异步代码,推荐首选。
- 高级场景:取消请求、生成器、异步迭代等。