跳转至

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
遗留项目维护 保持原有风格,或逐步重构

项目实践建议

如果团队项目中两种写法混用,建议统一规范:

  1. 统一使用 async/await:提升代码一致性
  2. 强制错误处理:ESLint 规则 require-atomic-updatesno-floating-promises
  3. 封装请求层:在 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 项目开发参考。

评论