await vs Promise.then():两种异步写法的区别
在 JavaScript 中处理异步操作时,我们经常会遇到两种写法:
// 写法一:async/await + try/catch
const getPackageListFromAPI = async () => {
try {
const res = await getPackageList();
packageList.value = res.data || [];
} catch (e) {
console.error("获取充值套餐列表失败", e);
}
};
// 写法二:Promise.then()
const getPackageListFromAPI = () => {
getPackageList().then((res) => {
packageList.value = res.data || [];
});
};
这两种写法功能等价,都能完成异步数据获取,但在错误处理、可读性和维护性上有显著差异。
核心区别一览
| 对比维度 | async/await + try/catch | Promise.then() |
|---|---|---|
| 错误捕获能力 | ✅ 捕获同步抛错 + await 异步失败 | ❌ 仅处理 then 回调内错误,Promise reject 易遗漏 |
| 代码可读性 | ✅ 像同步代码,逻辑线性清晰 | ⚠️ 回调嵌套,复杂场景易形成"回调地狱" |
| 调试体验 | ✅ 堆栈信息完整,断点友好 | ❌ 异步堆栈可能断层,调试困难 |
| 错误处理完整性 | ✅ try/catch 兜底所有异常 | ❌ 需手动追加 .catch(),否则 Unhandled Rejection |
| 现代推荐度 | ✅ ES2017+ 标准,主流首选 | ⚠️ 适用于简单链式场景 |
关键差异:错误处理
async/await 的错误捕获
const getPackageListFromAPI = async () => {
try {
const res = await getPackageList(); // ① 网络错误、② 接口返回 500、③ 代码异常
packageList.value = res.data || [];
} catch (e) {
// ✅ 上述所有情况都会进这里
console.error("获取失败", e);
ElMessage.error("网络异常,请稍后重试");
}
};
try/catch 能捕获:
- getPackageList() 内部抛出的同步错误
- Promise reject(网络失败、HTTP 错误状态码)
- await 之后的代码异常
Promise.then() 的隐患
const getPackageListFromAPI = () => {
getPackageList().then((res) => {
packageList.value = res.data || [];
});
// ❌ 如果 getPackageList() reject,这里没人处理
// 控制台报错:Uncaught (in promise) Error...
};
问题: - 网络失败时,错误未被捕获 - 用户看不到任何提示,页面处于空白/加载状态 - 生产环境可能触发全局错误监控
修正版本:
const getPackageListFromAPI = () => {
getPackageList()
.then((res) => {
packageList.value = res.data || [];
})
.catch((e) => {
// ✅ 必须手动追加 catch
console.error("获取失败", e);
});
};
可读性对比
复杂场景:多个异步操作
Promise.then() 链式:
getUser().then(user => {
return getOrders(user.id).then(orders => {
return getDetails(orders[0].id).then(details => {
console.log(details);
});
});
}).catch(e => {
console.error(e);
});
async/await:
try {
const user = await getUser();
const orders = await getOrders(user.id);
const details = await getDetails(orders[0].id);
console.log(details);
} catch (e) {
console.error(e);
}
async/await 的代码结构更接近同步思维,维护成本更低。
使用建议
| 场景 | 推荐写法 |
|---|---|
| Vue3 / React 现代项目 | async/await + try/catch |
| 单个简单异步操作 | 两者皆可,但记得 .catch() |
| 多个异步顺序执行 | async/await |
| 需要并行执行多个异步 | Promise.all() + async/await |
| 遗留项目维护 | 保持原有风格,或逐步重构 |
项目实践建议
如果团队项目中两种写法混用,建议统一规范:
- 统一使用 async/await:提升代码一致性
- 强制错误处理:ESLint 规则
require-atomic-updates、no-floating-promises - 封装请求层:在 axios 拦截器统一处理错误,业务层减少重复 try/catch
// 推荐:在请求拦截层统一处理
apiClient.interceptors.response.use(
res => res.data,
err => {
ElMessage.error(err.message || "请求失败");
return Promise.reject(err);
}
);
// 业务层可以更简洁
const loadData = async () => {
packageList.value = await getPackageList(); // 错误已在拦截器处理
};
总结
- 功能层面:两种写法都能实现异步操作
- 工程层面:
async/await在错误处理、可读性、可维护性上全面优于Promise.then() - 选型建议:新项目首选
async/await,旧项目逐步迁移,简单场景确保.catch()兜底
文章适用于 Vue3、React 及现代 JavaScript 项目开发参考。