【文件上传】pending代表什么意思。并发请求又是什么?

414 阅读3分钟

1️⃣ pending 的基本含义

  • pending 表示请求已经发出,但还没有收到服务器响应

  • 对于分片上传来说,每个块都是一个独立的请求(XHR 或 fetch / Axios)。

  • 在浏览器网络面板里,如果请求显示为 pending,说明:

    1. 浏览器已经开始发送这个分片数据(可能还在上传中)
    2. 服务器还没有返回响应(200/500/其它)
  • 它不是错误,只是表示请求还在进行中。


2️⃣ 分片上传中为什么会看到 pending

分片上传通常是大文件分多块上传,每块可能几 MB:

  • 网络上传时间长 → 请求还没完成 → 状态显示为 pending
  • 并发上传多个分片 → 多个请求同时 pending
  • 如果服务器处理慢,或者网络不稳定,也会让请求长时间 pending

简单类比

pending 就像你给快递下单了,快递员还没送到家,你还在等待快递状态更新。


3️⃣ 状态流

对于每个分片请求:

  1. pending → 请求发送中
  2. stalled/waiting → 网络延迟或服务器处理慢
  3. 200/201 → 上传成功,浏览器收到响应
  4. 4xx/5xx → 上传失败,需要重试

4️⃣ 注意事项

  • 如果很多请求长时间 pending,可能是:

    • 服务器处理慢或阻塞
    • 上传的文件块太大
    • 并发上传数量太多(浏览器限制每个域名最大并发请求,一般 6~10 个)
  • 分片上传优化:

    1. 限制并发上传块数量(例如 3~5 个同时上传)
    2. 合理分块大小(通常 1~5MB)
    3. 在前端设置超时或重试机制

🧩 一、背景:浏览器的并发限制

浏览器在网络层(HTTP/HTTPS)中,为了避免资源竞争、节流带宽,对同一个域名的并发连接数是有限制的。

一般是:

浏览器每个域名的并发限制
Chrome / Edge~6
Firefox~6
Safari~6
老版 IE4

也就是说:

对同一个服务器(域名),浏览器最多只能同时维持 6 个请求


📦 二、当你上传大文件时

如果你把一个大文件拆成很多块(比如 100 块),然后直接这样并发上传:

for (let i = 0; i < 100; i++) {
  uploadChunk(i); // 每个 uploadChunk 都是一个 fetch / xhr
}

那会发生什么?

  1. 浏览器会立刻尝试发出 100 个请求;
  2. 前 6 个进入**“正在上传”状态**;
  3. 剩下 94 个会被放进请求队列(排队等待)
  4. 当前面的请求完成后,新的才会被“接力”发出去。

🔥 三、这会带来什么问题?

1️⃣ 页面卡顿 / UI 延迟

  • 如果你没正确处理异步队列、或者没有让事件循环喘息(比如 await Promise.race 控制节奏),
    浏览器主线程会因为大量任务排队,导致 UI 卡顿。

2️⃣ 进度条不准

  • 太多请求排队在浏览器内部队列中,onprogress / upload.onprogress 不会立即触发。
    你会以为“卡住了”,其实只是还没轮到上传。

3️⃣ 请求失败或被中止

  • 某些浏览器或服务器会中止超出连接数的请求;
  • 有时服务器也有限流策略(比如 Nginx、OSS、S3 等会限制同一 IP 同时上传数)。

4️⃣ 串口“堵死”

  • 如果页面上还有别的请求(例如接口轮询、心跳、WebSocket reconnect),
    它们也可能因为连接被占满而卡住。

🧩 四、正确做法:控制并发数

👉 这时我们就要用前面那个函数:

async function concurrentUpload(tasks, concurrency = 3) {
  const results = [];
  const executing = new Set();

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

    const clean = () => executing.delete(p);
    p.then(clean).catch(clean);

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

  return Promise.all(results);
}

使用方式:

const tasks = chunks.map((chunk, i) => () => uploadChunk(chunk, i));
concurrentUpload(tasks, 3);

🔹 优点:

  • 永远只会占用 3 个 HTTP 连接;
  • 有任务完成后再补新的;
  • 进度更新平滑;
  • 页面不卡;
  • 上传性能反而更稳定。