Async/Await:让异步像同步一样简单

上一期我们学会了用 Promise 链式调用来摆脱回调地狱。

今天我们再往前迈一步,用 async/await 把异步代码写得像同步代码一样直观

async/await 是 ES2017 引入的语法糖,本质上还是 Promise,但极大提升了代码的可读性。

1. 基本写法

javascript 复制代码
// 普通 Promise 写法
function fetchUser() {
  return fetch('/api/user')
    .then(res => res.json())
    .then(data => data.user);
}

// async/await 写法
async function fetchUser() {
  const res = await fetch('/api/user');
  const data = await res.json();
  return data.user;
}

关键点

  • 在函数前加 async,这个函数就自动返回一个 Promise
  • 在 Promise 前面加 await,表示"等到这个 Promise 完成再继续往下走"
  • 代码从上到下顺序执行,像写同步代码一样

2. 错误处理:使用 try...catch

javascript 复制代码
async function loginFlow(username, password) {
  try {
    const token = await login(username, password);
    const user = await getUserInfo(token);
    const products = await getRecommendations(user.level);
    
    renderProducts(products);
    showSuccess("欢迎回来!");
  } catch (error) {
    console.error("登录流程失败:", error);
    showError("出错了,请稍后重试");
  }
}

优点

  • 只需要一个 try...catch,就能捕获整个流程中任意一步的错误
  • 错误位置清晰,栈追踪更有意义
  • 比 Promise 的 .catch() 更接近我们熟悉的同步错误处理方式

3. 常见使用场景

3.1 顺序执行多个异步操作

javascript 复制代码
async function processOrder() {
  const order = await getOrderFromDB(orderId);
  const payment = await processPayment(order.amount);
  const shipping = await createShippingLabel(payment);
  await sendConfirmationEmail(shipping.trackingNumber);
  
  return shipping;
}

3.2 与 Promise.all 结合实现并发

javascript 复制代码
async function loadUserData(userId) {
  const [profile, posts, friends] = await Promise.all([
    fetch(`/api/profile/${userId}`),
    fetch(`/api/posts/${userId}`),
    fetch(`/api/friends/${userId}`)
  ]);
  
  // 三个请求并发发起,最后一起等待完成
  const profileData = await profile.json();
  const postsData = await posts.json();
  const friendsData = await friends.json();
  
  return { profileData, postsData, friendsData };
}

3.3 在循环中使用 await(注意性能)

javascript 复制代码
// 顺序执行(适合需要前一步结果的情况)
async function processItems(items) {
  for (const item of items) {
    const result = await processSingleItem(item);
    saveResult(result);
  }
}

// 如果不需要顺序,可以先 Promise.all 再循环
async function processItemsInParallel(items) {
  const promises = items.map(item => processSingleItem(item));
  const results = await Promise.all(promises);
  results.forEach(saveResult);
}

4. 常见陷阱与注意事项

问题 错误写法 正确写法 说明
在循环里 await 导致串行慢 forEach(item => await fn(item)) for...ofPromise.all forEach 不等待
忘记 await const data = fetch(url) const data = await fetch(url) 否则得到 Promise 对象
try...catch 范围太小 只 catch 单行 包住整个流程 否则错误漏掉
顶层 await 不支持(旧环境) 包在 async 函数里 现代模块支持顶层 await
性能误用 所有操作都 await 能并行的用 Promise.all 避免不必要的等待

5. 真实业务对比

Promise 链式

js 复制代码
login(username, pwd)
  .then(token => getUserInfo(token))
  .then(user => getRecommendations(user.level))
  .then(products => render(products))
  .catch(err => showError(err));

async/await

js 复制代码
async function start() {
  try {
    const token = await login(username, pwd);
    const user = await getUserInfo(token);
    const products = await getRecommendations(user.level);
    render(products);
  } catch (err) {
    showError(err);
  }
}

大多数开发者认为后者更清晰、更容易维护。

6. 小结:async/await 的核心价值

  • 可读性:代码结构接近日常同步思维
  • 错误处理:统一的 try...catch
  • 调试友好:断点更容易命中预期位置
  • 与 Promise 完全兼容:该并发时用 Promise.all,该顺序时用 await

一句话总结

async/await 让异步代码看起来像同步代码,但依然保留了异步非阻塞的本质。

下一期我们将把学到的异步知识应用到实际网络请求中:
Fetch API 与异步网络请求 ------ 现代浏览器中最常用的数据获取方式。

我们下期见~

留言区互动:

你更喜欢 Promise 链式还是 async/await?

有没有在项目中因为 async/await 写法而修复过 bug 的经历?

相关推荐
听风说图1 小时前
从 JavaScript 到 WGSL:用渐变渲染理解 GPU 编程思维
前端
float_六七1 小时前
CSS行内盒子:30字掌握核心特性
前端·css
倔强的钧仔1 小时前
拒绝废话!前端开发中最常用的 10 个 ES6 特性(附极简代码)
前端·javascript·面试
喔烨鸭1 小时前
vue3中使用原生表格展示数据
前端·javascript·vue.js
软件开发技术深度爱好者2 小时前
JavaScript的p5.js库坐标系图解
开发语言·前端·javascript
如果你好2 小时前
JavaScript 对象属性遍历Object.entries Object.keys:6 种常用方法详解与对比
javascript
donecoding2 小时前
CSS的"双胞胎"陷阱:那些看似对称却暗藏玄机的属性对
前端·css·代码规范
胖鱼罐头2 小时前
JavaScript 数据类型完全指南
前端·javascript
代码猎人2 小时前
map和Object有什么区别
前端