并发控制器,批量任务的必备工具
页面里有 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 个请求在执行
- 任务完成后自动执行下一个排队的任务
- 不会占满浏览器连接数,其他业务请求不受影响
- 页面主线程不会被大量解码任务阻塞,滚动、点击依然流畅
拓展版本
后来我还给这个调度器加了几个功能:动态调整并发数、清空待执行队列、等待所有任务完成、销毁清理。这些在文件上传、批量请求、资源加载等场景里都很实用。
并发控制这件事,本质上就是"把有限的资源合理分配"。不只是在图片加载里,在接口请求、文件上传、数据批量处理等场景都能用上。
