Full Stack SWE · JavaScript Coding

Databricks 客户端Fetch:并行请求、批次与部分失败

把逐个请求改成有界并行批次,保留输入顺序并正确处理HTTP与JSON错误。

题目重点:把逐个请求改成有界并行批次,保留输入顺序并正确处理HTTP与JSON错误。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 明确问客户端fetch data→JSON,parallel及batch优化,但没有API schema、服务端batch endpoint或并发上限。练习采用单URL请求,每批最多B个并行,批次串行,不虚构服务端批量协议。
  • 输出每个已调度URL的fulfilled/value或rejected/error,按输入顺序;重复URL视为两次请求,不去重。注入fetch便于独立验证;不保证某结果字段结构来自实际接口。

解题路线

  1. 串行await一条再发下一条,等待时间接近各延迟之和;同批并行可把该批等待缩至最慢请求附近。但无界Promise.all(urls.map(...))会同时调度所有请求,占用连接、限流预算与内存。
  2. 按B切片,Promise.allSettled并行请求并解析JSON;每批结束再发下一批。响应HTTP错误必须检查response.ok,不能把fetch正常返回Response视为业务成功;JSON解析也可能失败。
  3. allSettled保证一条失败不抹掉同批成功;返回顺序按输入promise顺序,不依赖完成顺序。每条保存明确失败信息,后续重试只针对可重试项,并需要总deadline和退避预算。
  4. 固定批次有队头等待:其中一条慢请求会让下一批所有任务延后。更均匀吞吐可用B个worker共享任务索引,某worker完成即领下一项,结果仍写原索引;这是后续优化,不把当前代码称为持续满负荷pool。
  5. 这里batch是客户端调度分组。真正HTTP batch需要服务端协议支持每子项ID、错误、响应大小与重试语义,才能减少请求数;不能仅把数组拼成JSON就假设接口支持。
  6. 传入AbortSignal给全部fetch;批次完成后若已abort,不调度下一批。取消不保证服务器没处理请求,示例结果只覆盖已调度部分;若需要每输入一个结果,应给未调度项单独cancelled状态。

手动走一遍

URL[a,b,c,d,e],B2时先并发a/b,再c/d,再e。b返回503被记为rejected;a正常JSON仍保留。即使b先完成,结果也按a、b顺序。若c解析坏JSON,其HTTP200也不是成功。

JavaScript 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

export async function fetchJSONBatches(urls, { batchSize = 4, fetchImpl = fetch, signal } = {}) {
  if (!Number.isInteger(batchSize) || batchSize < 1) throw new RangeError("positive batchSize");
  const results = [];
  for (let offset = 0; offset < urls.length; offset += batchSize) {
    const batch = urls.slice(offset, offset + batchSize);
    const outcomes = await Promise.allSettled(batch.map(async (url) => {
      const response = await fetchImpl(url, { signal });
      if (!response.ok) throw new Error("HTTP " + response.status);
      return await response.json();
    }));
    for (let i = 0; i < batch.length; i++) {
      const outcome = outcomes[i];
      results.push(outcome.status === "fulfilled"
        ? { url: batch[i], status: "fulfilled", value: outcome.value }
        : { url: batch[i], status: "rejected", error: String(outcome.reason?.message ?? outcome.reason) });
    }
    if (signal?.aborted) break;
  }
  return results;
}

复杂度

按N个URL、B上限,结果O(N)空间,活动请求/批临时状态O(B),实际响应体总大小需另计。均匀延迟L的理想等待约ceil(N/B)×L,不能保证网络/限流下严格N/B倍加速。客户端分批仍发N个HTTP请求。

易错点与追问

容易失分的边界

  • fetch遇404/503通常不会自动reject,遗漏ok检查。
  • 把并行完成顺序当输入顺序。
  • 将客户端批次当服务端batch协议,虚报减少请求数量。

继续深挖

  • 如何实现持续worker池并证明活跃请求≤B?
  • 哪些GET失败可重试?若换成POST,重试需要什么幂等键?
  • 海量JSON是否应边处理边释放,而不是累积全部结果?

技术参考

✓已复制微信号!