Skip to content

并发请求

并发请求指的是:多个异步请求互不依赖时,同时发起,再按需要统一等待结果,而不是一个请求结束后再发下一个。

什么场景会用到

常见情况包括:

  • 页面初始化时同时请求多个独立接口
  • 批量读取多个资源
  • 多个任务可以并行执行,不存在前后依赖

串行和并发的区别

下面是串行:

js
const user = await getUser()
const settings = await getSettings()
const permissions = await getPermissions()

这种写法会一个接一个地等待。

下面是并发:

js
const userPromise = getUser()
const settingsPromise = getSettings()
const permissionsPromise = getPermissions()

const [user, settings, permissions] = await Promise.all([userPromise, settingsPromise, permissionsPromise])

这里三个请求会尽早一起发出。

最常见的方式:Promise.all

js
const [user, settings] = await Promise.all([getUser(), getSettings()])

Promise.all 的规则可以直接记成:

  • 全部成功才成功
  • 只要一个失败就整体失败
  • 返回结果顺序和传入顺序一致,不看完成先后

一个基础例子

js
async function loadPageData() {
  const [user, posts, notices] = await Promise.all([fetchUser(), fetchPosts(), fetchNotices()])

  return { user, posts, notices }
}

这适合“页面展示依赖全部数据都到齐”的场景。

不是所有场景都该用 Promise.all

如果希望“每个请求都执行完,再统一看结果”,更适合 Promise.allSettled

js
const results = await Promise.allSettled([taskA(), taskB(), taskC()])

这适合:

  • 批量任务统计
  • 局部失败可接受
  • 不希望一个失败打断全部流程

并发不等于无限制同时发送

这是一个高频误区。

“并发”只表示多个任务重叠进行,不等于可以无上限同时发起。

如果一次性发出太多请求,可能带来:

  • 服务端压力增大
  • 浏览器连接数受限
  • 页面瞬时资源占用过高
  • 失败重试雪上加霜

需要限流时怎么做

如果任务很多,通常要做“限并发”而不是“全并发”。

可以把它理解成:同一时间只允许固定数量的请求同时进行。

js
async function runWithLimit(tasks, limit) {
  const results = []
  const executing = new Set()

  for (const task of tasks) {
    const promise = Promise.resolve().then(task)
    results.push(promise)
    executing.add(promise)

    promise.finally(() => {
      executing.delete(promise)
    })

    if (executing.size >= limit) {
      await Promise.race(executing)
    }
  }

  return Promise.all(results)
}

这类模式适合批量上传、批量抓取、批量同步任务。

错误处理要先想清楚

并发请求最容易忽略的是“失败策略”:

  • 是一个失败就整体中断
  • 还是保留成功结果,单独记录失败项
  • 是否需要重试
  • 是否需要超时控制

例如:

js
try {
  const [user, settings] = await Promise.all([getUser(), getSettings()])
} catch (error) {
  console.error('load failed', error)
}

如果希望部分成功也保留结果,就不该直接只用 Promise.all

await 连写不一定是并发

这也是最常见的误区之一。

js
const a = await fetchA()
const b = await fetchB()

这通常是串行,不是并发。

真正的并发写法通常是先拿到 Promise,再统一等待:

js
const aPromise = fetchA()
const bPromise = fetchB()

const [a, b] = await Promise.all([aPromise, bPromise])

使用建议

  • 任务互不依赖时,优先考虑并发。
  • 全部成功才继续时,用 Promise.all
  • 需要保留每项结果时,用 Promise.allSettled
  • 任务数量很大时,不要直接全并发,优先考虑限并发。

基于 MIT 许可发布