浏览器同域fetch并发上限通常为6,盲目并发反致排队超时;应采用可控队列限流,如滑动窗口式每次≤3个请求、完成即补发,并封装支持动态调优的请求池。

浏览器对同域 fetch 有硬性并发上限(通常为 6 个),盲目并发大量请求不仅不会提速,反而会触发排队、超时、重试雪崩。真正有效的限流不是“压着发”,而是用可控队列让请求按节奏进、稳稳出。
用滑动窗口代替 Promise.all
别写 Promise.all(chunks.map(fetch))——这会让全部请求瞬间进入等待队列,超出浏览器限制的部分直接卡住。正确做法是手动控制“同时最多跑几个”:
- 每次只发起 ≤3 个 fetch(推荐值,兼顾吞吐与稳定性)
- 每个请求完成(无论成功或失败)后,立刻检查队列,补发下一个
- 用
async/await + for...of循环最直观,避免回调嵌套和计数器死锁
带状态标识的分片上传队列
大文件分片上传尤其需要可中断、可续传、可定位。每片必须携带上下文信息:
- 请求头加
Content-Range: bytes 0-5242879/10485759,服务端靠它写入准确位置 - 每个 fetch 携带唯一标识:
filename、chunkIndex、totalChunks - 失败只重试当前片,不回滚整文件;服务端需支持幂等写入(同一片重复提交不覆盖错位)
封装可复用的限流请求池
把并发控制逻辑抽成类或函数,后续复用省心:
- 维护
activeCount(运行中请求数)和queue(待发任务队列) -
add(requestFn)接收异步函数,返回 Promise,内部自动调度 - 支持动态调整最大并发数(如网络弱时降为 2,Wi-Fi 下升为 4)
- 暴露
activeCount和pendingCount便于监控或 UI 反馈
避免常见死锁陷阱
别用 while (queue.length) { if (limit > 0) { fetch().finally(() => limit++) } } 这类写法——limit++ 是微任务,而 while 是同步阻塞,会导致主线程卡死、回调永远不执行。
- 所有调度必须基于 Promise 状态流转(.then / .catch / .finally)驱动
- 用递归调用或队列
_dequeue()方法,而不是轮询判断 - 每个请求完成后显式触发下一轮,确保事件循环有机会清空微任务队列











