一个小小的 await 顺序,可能让你的接口总耗时翻倍

在现代前端开发中,axios 搭配 async/await 几乎是请求接口的标配写法。但很多人可能没意识到:同样是写 await,两个接口的完成时间可能相差一倍

场景复现

假设你有两个接口:接口 A 和接口 B,每个都需要较长时间才能返回数据(比如各需 10 秒)。

现在你需要在同一个 async 函数里依次请求它们,最后拿到两个数据。

来看这段代码:

async function fetchData() {
  const resA = await axios.get('/api/A'); // 耗时较长
  const resB = await axios.get('/api/B'); // 耗时较长
  return [resA, resB];
}

问题:这个函数的总执行时间是多长?

  • 等于单个接口的时间?
  • 还是两个接口时间之和?

答案是 两个接口时间之和(例如 10+10=20 秒)。

因为 await阻塞后续代码的执行:先等 A 完成,再开始请求 B,两个请求是串行的。

真正的“同时请求”:只需要最慢那个接口的时间

很多时候,接口 A 和接口 B 之间并没有依赖关系(B 不需要 A 的返回结果)。这种情况下,完全可以让它们并行发起,从而把总耗时压缩到 单个最慢接口的耗时

并行写法一:先发起请求,后 await

async function fetchData() {
  const promiseA = axios.get('/api/A');  // 立即发起,不等待
  const promiseB = axios.get('/api/B');  // 立即发起,不等待
  const [resA, resB] = await Promise.all([promiseA, promiseB]);
  return [resA, resB];
}

并行写法二:直接 Promise.all

async function fetchData() {
  const [resA, resB] = await Promise.all([
    axios.get('/api/A'),
    axios.get('/api/B')
  ]);
  return [resA, resB];
}

两种写法效果完全一样:两个请求几乎同时发出,总耗时 ≈ max(耗时A, 耗时B)。

对比一览

写法 执行方式 总耗时
顺序 await(A 完再 B) 串行 耗时A + 耗时B
Promise.all + 同时发起 并行 max(耗时A, 耗时B)

什么时候该用并行,什么时候该用串行?

  • 用并行:两个接口互不依赖(比如同时获取用户信息、系统配置、商品列表)。
    效果:更快,用户少等待。

  • 用串行:后一个接口依赖前一个接口的返回结果(比如先登录获得 token,再用 token 请求用户详情)。
    效果:逻辑正确

一个常见的坑

新手容易写成这样:

async function fetchData() {
  const resA = await axios.get('/api/A');
  const resB = await axios.get('/api/B');  // 没依赖也等A结束
  // ...
}

或者在循环里无意识地串行:

const ids = [1, 2, 3];
for (const id of ids) {
  await axios.get(`/api/item/${id}`);  // 三个请求串行,总时间累加
}

如果这些请求彼此独立,应该用 Promise.allPromise.allSettled 并行处理。

小结

  • async/await 本身不决定串行或并行——决定权在于你何时使用 await
  • 默认顺序 await = 串行,总耗时 = 各接口耗时之和。
  • 想让多个请求同时发出、总耗时取最长者,请使用 Promise.all

记住这个区别,下次写接口请求时就能帮用户省下成倍的等待时间 —— 积少成多,体验优化就从这一个小细节开始。