Skip to content

并发控制器,批量任务的必备工具

页面里有 100 张高清图片,如果用 <img> 直接渲染,浏览器会同时发起大量请求,不仅拖慢加载速度,还会导致页面频繁触发 Layout 和 Paint,造成卡顿。

原因很简单:浏览器对同一域名的并发请求有限制(通常 6-8 个),100 个请求同时发出去,不仅把连接数占满了,其他正常业务请求也发不出去了。

解决方案:并发控制器

核心思路是维护一个任务队列,控制同时执行的任务数量。当一个任务完成时,自动从队列中取出下一个任务执行。

javascript
class TaskScheduler {
  tasks = [];
  runningCount = 0;
  maxRunning = Infinity;

  constructor(maxRunning = Infinity) {
    this.maxRunning = maxRunning === 0 ? Infinity : maxRunning;
  }

  run() {
    if (this.runningCount >= this.maxRunning) return;
    if (this.tasks.length === 0) return;

    this.runningCount++;
    const task = this.tasks.shift();

    task().finally(() => {
      this.runningCount--;
      this.run();
    });
  }

  addTask(task) {
    return new Promise((resolve, reject) => {
      this.tasks.push(() => task().then(resolve).catch(reject));
      this.run();
    });
  }
}

使用方式

javascript
const loader = new TaskScheduler(4); // 同时最多加载 4 张

const urls = [
  'https://example.com/hd/1.jpg',
  'https://example.com/hd/2.jpg',
  // ... 100 张图
];

urls.forEach(url => {
  loader.addTask(() => preloadImage(url));
});

function preloadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = resolve;
    img.onerror = reject;
    img.src = url;
  });
}

执行效果

  • 同时最多只有 4 个请求在执行
  • 任务完成后自动执行下一个排队的任务
  • 不会占满浏览器连接数,其他业务请求不受影响
  • 页面主线程不会被大量解码任务阻塞,滚动、点击依然流畅

拓展版本

后来我还给这个调度器加了几个功能:动态调整并发数、清空待执行队列、等待所有任务完成、销毁清理。这些在文件上传、批量请求、资源加载等场景里都很实用。

并发控制这件事,本质上就是"把有限的资源合理分配"。不只是在图片加载里,在接口请求、文件上传、数据批量处理等场景都能用上。