人人都会AI编程

同步写法的异步代码

更新时间:2026-07-11

async/await 是 ES2017 引入的语法糖,它让你能用写同步代码的方式去处理异步逻辑,彻底告别 Promise 的链式调用和回调地狱。

从 Promise 到 async/await

假设有一个分步异步操作:先获取用户信息,再根据用户 ID 获取订单列表。

传统 Promise 写法

function getUser() {
  return fetch('/api/user').then(res => res.json());
}

function getOrders(userId) {
  return fetch(`/api/orders?userId=${userId}`).then(res => res.json());
}

// 链式调用
getUser()
  .then(user => {
    console.log('用户:', user);
    return getOrders(user.id);
  })
  .then(orders => {
    console.log('订单:', orders);
  })
  .catch(err => {
    console.error('出错了:', err);
  });

同一个功能用 async/await 改写,逻辑立刻变得清晰:

async function loadUserOrders() {
  try {
    const user = await getUser();       // 等待用户数据
    console.log('用户:', user);
    const orders = await getOrders(user.id);  // 等待订单数据
    console.log('订单:', orders);
  } catch (err) {
    console.error('出错了:', err);
  }
}

loadUserOrders();
  • async 关键字标记函数为异步函数,它永远返回一个 Promise。
  • await 会暂停当前 async 函数的执行,等待后面的 Promise 完成,并返回其结果。
  • 如果 Promise 被拒绝,await 会抛出错误,可以用 try...catch 捕获。

代码阅读顺序,就是执行顺序

这才是 async/await 最实用的价值——从上到下读代码,逻辑一目了然,不再需要来回跳转 .then() 块。对于复杂的条件分支、循环异步操作,优势更加明显。

// 根据用户权限决定下一步操作
async function dashboard(userId) {
  const user = await fetchUser(userId);
  if (user.role === 'admin') {
    const reports = await fetchReports();
    return { user, reports };
  } else {
    const tasks = await fetchTasks(userId);
    return { user, tasks };
  }
}

如果换成 Promise,就需要在多个 .then() 里嵌套条件,可读性急剧下降。

错误处理:用 try...catch 统一捕获

同步风格的另一个好处就是错误处理回归经典模式。无论是网络错误、JSON 解析错误,还是业务抛出的异常,都能在同一个 catch 块中处理。

async function safeFetch(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('请求失败:', error.message);
    // 可以返回兜底数据或重新抛出
    return null;
  }
}

实际开发中常用的“坑”和最佳实践

1. 不要在循环中盲目使用 await
如果循环中的每次迭代不相互依赖,同步等待会不必要地串行,延长总耗时。

// ❌ 依次等待,总耗时 = 每次耗时累加
for (const id of ids) {
  const data = await fetchData(id);
}

// ✅ 并行执行,总耗时 ≈ 最慢的那一个
const promises = ids.map(id => fetchData(id));
const results = await Promise.all(promises);

2. await 只能在 async 函数内使用
顶级作用域(例如模块顶层)在没有包裹 async 函数时不能直接写 await。不过,现代 JavaScript 已经支持了 top-level await(ES2022),在 ES Module 中可以直接在模块顶层使用 await,无需封装。

3. async 函数永远返回 Promise
即使你 return 一个普通值,它也会被自动包装成 Promise.resolve(value)。这意味着调用方仍然需要 await.then() 来获取结果,否则得到的是一个 Promise 对象。

4. 忘记 await 的常见错误
如果忘了写 await,得到的只会是一个 Pending 状态的 Promise 对象,而不是实际的返回值。

const user = getUser();  // user 是 Promise 对象,不是数据
console.log(user.name);  // undefined

总结

async/await 本质上是 Promise + Generator 的语法糖,并没有带来全新的异步机制,但它极大提升了异步代码的可读性和可维护性。在实际开发中,它能让你用最直观的方式编排异步流程,让业务逻辑回归线性思维,而不必在回调链中来回跳转。