一、Promise 与 async/await 的核心区别
1. 设计定位与语法结构
-
Promise
-
是 ES6 引入的异步容器对象,代表未来完成的操作结果,状态不可逆(Pending → Fulfilled/Rejected)。
-
链式调用:通过
.then()
处理成功结果、.catch()
捕获错误、.finally()
执行清理逻辑。
javascript fetchData() .then(data => process(data)) .catch(err => console.error(err));
-
缺点:
-
链式调用在复杂逻辑中易形成“回调地狱”(视觉嵌套);
-
中间值传递需通过嵌套作用域实现,代码冗余。
- async/await
-
是 ES2017 基于 Promise 的语法糖,用同步写法实现异步逻辑。
-
同步风格:
async
标记函数返回 Promise,await
暂停执行直至 Promise 完成:
javascript async function getData() { const data = await fetchData(); // 等待 Promise 完成 return process(data); }
-
优势:
-
代码线性化,消除嵌套;
-
直接使用变量存储中间结果。
2. 执行流程差异
场景 | Promise | async/await |
---|---|---|
创建后代码执行 | 同步代码继续执行,不阻塞 | await 暂停所在函数,释放调用栈 |
回调触发时机 | 回调进入微任务队列,当前任务后执行 | await 后代码在 Promise 完成后执行 |
错误传播 | 需手动链式调用 .catch() | 可通过 try/catch 直接捕获 |
示例对比:
// Promise:后续代码立即执行
const p = fetchData();
console.log("继续执行"); // 先输出
p.then(data => console.log(data));
// async/await:函数内暂停
async function main() {
const data = await fetchData(); // 此处暂停
console.log(data); // 后输出
}
console.log("继续执行"); // 先输出
3. 可读性与调试
- Promise:
- 调试器无法逐步跟踪
.then()
链; - 错误堆栈可能缺失上下文。
- async/await:
- 支持同步式断点调试;
- 错误堆栈包含完整函数调用链。
二、异步错误捕获方法
1. Promise 的错误捕获
.catch()
方法:链式调用末尾捕获错误。
fetchData()
.then(data => process(data))
.catch(err => console.error("全局捕获:", err)); // 捕获链中任意错误
-
注意事项:
-
未处理的 Promise 错误会导致 “UnhandledPromiseRejection” ;
-
异步回调中抛错(如
setTimeout
)无法被.catch()
捕获:
javascript new Promise((resolve) => { setTimeout(() => { throw new Error("异步错误"); }, 1000); }).catch(err => console.log(err)); // 无法捕获!
2. async/await 的错误捕获
try/catch
块:仿同步代码捕获错误。
async function loadData() {
try {
const data = await fetchData();
const result = await process(data);
} catch (err) {
console.error("操作失败:", err); // 捕获所有 await 错误
} finally {
cleanUp(); // 必执行逻辑
}
}
并行错误处理:结合 Promise.all()
+ try/catch
:
async function fetchMulti() {
try {
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts()
]);
} catch (err) {
console.error("并行请求出错:", err); // 任一失败即捕获
}
}
3. 关键实践建议
- 避免混合使用:
- 在 async 函数中无需再用
.then()
,直接用await
简化。- 全局兜底:
- 浏览器中监听
unhandledrejection
事件捕获未处理的 Promise 错误:
javascript window.addEventListener("unhandledrejection", e => { console.error("未捕获的Promise错误:", e.reason); e.preventDefault(); // 阻止控制台报错 });
三、如何根据场景选择?
场景 | 推荐方案 | 原因 |
---|---|---|
简单异步链(<3 步) | Promise 链式调用 | 代码简洁,无需额外函数封装 |
复杂逻辑(依赖中间值、错误分支) | async/await + try/catch | 逻辑线性化,错误集中处理 |
并行独立任务 | Promise.all() + await | 最大化并发效率,统一错误捕获 |
需取消的异步操作 | 第三方库(如 RxJS) | 原生 Promise 和 async/await 均不支持取消 |
总结
- 核心区别:
Promise 是基础异步模型,通过状态机和链式调用管理异步;async/await 是其语法增强,以同步写法提升可读性和可维护性。 - 错误捕获:
Promise 依赖.catch()
链式捕获,async/await 通过try/catch
实现结构化错误处理,后者更符合直觉。 - 演进关系:
async/await 本质上仍是 Promise,但通过语法层优化解决了 Promise 在复杂场景下的工程化痛点。