回调函数(Callback)、Promise 和 async/await 都是 JavaScript 中处理 异步编程 的方式。
一、定义和基本写法
1. 回调函数(Callback)
概念:
把一个函数作为参数传给另一个函数,当异步操作完成后,再调用这个函数。
示例:
javascript
function fetchData(callback) {
setTimeout(() => {
callback("数据已加载");
}, 1000);
}
fetchData(function (data) {
console.log(data); // 输出:数据已加载
});
-
优点:
- 语法简单,广泛使用。
-
缺点:
-
容易出现"回调地狱"或"嵌套地狱"。
-
错误处理繁琐,容易遗漏。
-
2. Promise
概念:
Promise 是一个代表未来值的对象。它有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)。
示例:
javascript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("数据已加载");
// 或者 reject("加载失败");
}, 1000);
});
}
fetchData()
.then((data) => console.log(data)) // 成功处理
.catch((err) => console.error(err)) // 错误处理
.finally(() => console.log("结束")); // 无论成功或失败都执行
-
优点:
-
链式调用,减少嵌套。
-
内置 .then(), .catch(), .finally() 更好组织代码。
-
-
缺点:
-
多层 .then() 嵌套仍然不够直观。
-
错误捕获仍需特别注意。
-
3. async / await
概念:
是基于 Promise 的语法糖,让异步代码写起来更像同步,更简洁清晰。
示例:
javascript
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("数据已加载");
}, 1000);
});
}
async function getData() {
try {
const data = await fetchData();
console.log(data); // 输出:数据已加载
} catch (err) {
console.error(err); // 错误捕获
} finally {
console.log("结束");
}
}
getData();
-
优点:
-
更接近同步代码的书写方式,逻辑更清晰。
-
错误处理使用 try/catch,和同步代码一致。
-
-
缺点:
-
不能用在顶层作用域中(必须放在 async 函数里)。
-
同时执行多个异步任务时不如 Promise.all 等组合方法直观。
-
二、优缺点对比
特性 | 回调函数 | Promise | async/await |
---|---|---|---|
引入年代 | 最早 | ES6(2015) | ES8(2017) |
可读性 | 差(容易嵌套) | 中等(链式) | 高(接近同步代码) |
控制流程 | 回调地狱(嵌套多层) | 链式调用 .then() |
线性流程,最直观 |
错误处理 | 不统一(靠手动处理) | .catch() 统一处理 |
try/catch 统一处理 |
取消操作 | 不方便 | 可结合 AbortController |
可结合 AbortController |
并发执行 | 复杂 | Promise.all 等很方便 |
可搭配 Promise 并发执行 |
兼容性 | 老浏览器兼容好 | IE11 及以上 | 需要 Babel 转码支持 |
三、使用场景推荐
场景 | 推荐用法 |
---|---|
旧项目/库或兼容性要求高 | 回调函数 |
多个异步并发或链式处理 | Promise |
写异步流程、结构清晰、易维护 | async / await |
四、实际对比举例
- 回调嵌套(回调地狱)
javascript
getUser(id, function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
console.log(comments);
});
});
});
- Promise 链式优化
javascript
getUser(id)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));
- async/await 优雅写法
javascript
async function showComments(id) {
try {
const user = await getUser(id);
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
console.log(comments);
} catch (err) {
console.error(err);
}
}
五、总结
-
回调函数 是最原始的异步方式,但会导致 回调地狱。
-
Promise 更加优雅,支持 链式调用与统一错误处理。
-
async/await 是基于 Promise 的语法糖,让异步代码写得像同步一样,可读性最强。