5.实现并发任务
一、基础方案:直接并发(无限制)
使用 Promise.all
或 Promise.allSettled
直接触发多个异步任务,但不控制并发数量,适用于少量任务或浏览器自动处理并发限制的场景。
// 示例:同时发起多个请求(无并发控制)
async function runConcurrentTasks(tasks) {
const results = await Promise.all(tasks.map((task) => task()));
return results;
}
// 使用
const tasks = [fetchData1, fetchData2, fetchData3];
runConcurrentTasks(tasks).then((results) => {
console.log("所有任务完成:", results);
});
一次性触发所有任务,由浏览器底层控制实际并发数(如 HTTP/1.1 同一域名下默认最多 6 个并发)。
若任务数量过多,可能导致性能问题或请求被阻塞。
二、手动控制并发数量,使用队列
通过队列和计数器限制同时运行的异步任务数量,避免资源耗尽。
- 实现一个并发控制器
class ConcurrencyController {
constructor(maxConcurrency) {
this.maxConcurrency = maxConcurrency; // 最大并发数
this.taskQueue = []; // 任务队列
this.runningCount = 0; // 当前运行中的任务数
}
// 添加任务到队列
addTask(task) {
return new Promise((resolve, reject) => {
this.taskQueue.push({ task, resolve, reject });
this._run();
});
}
// 执行任务(内部方法)
_run() {
while (
this.runningCount < this.maxConcurrency &&
this.taskQueue.length > 0
) {
const { task, resolve, reject } = this.taskQueue.shift();
this.runningCount++;
task()
.then(resolve)
.catch(reject)
.finally(() => {
this.runningCount--;
this._run(); // 递归触发下一个任务
});
}
}
}
- 使用示例
// 初始化控制器(最大并发数=2)
const controller = new ConcurrencyController(2);
// 模拟异步任务(如API请求)
function createTask(id, delay) {
return () =>
new Promise((resolve) => {
setTimeout(() => {
console.log(`任务 ${id} 完成`);
resolve(id);
}, delay);
});
}
// 添加5个任务(每个任务耗时不同)
const tasks = [
createTask(1, 1000),
createTask(2, 500),
createTask(3, 800),
createTask(4, 300),
createTask(5, 1200),
];
// 并发执行任务(最多同时2个)
tasks.forEach((task) => {
controller.addTask(task).then((result) => {
console.log("任务结果:", result);
});
});
// 输出:
// 任务 2 完成 (500ms后)
// 任务 4 完成 (300ms后,任务3被提前触发)
// 任务 3 完成 (800ms后)
// 任务 1 完成 (1000ms后)
// 任务 5 完成 (1200ms后)
精准控制并发数(如限制为 2,避免资源过载)。
任务按添加顺序启动,但完成顺序取决于执行时间。
自动排队,确保始终不超过最大并发数。
三、高级场景:动态优先级 & 错误处理
扩展控制器以支持任务优先级和错误重试机制。
- 实现一个优先级队列
class AdvancedConcurrencyController extends ConcurrencyController {
addTask(task, priority = 0) {
return new Promise((resolve, reject) => {
// 根据优先级插入队列(priority值越小优先级越高)
const queueItem = { task, resolve, reject, priority };
let index = this.taskQueue.findIndex((item) => item.priority > priority);
index = index === -1 ? this.taskQueue.length : index;
this.taskQueue.splice(index, 0, queueItem);
this._run();
});
}
}
// 使用示例
const advancedController = new AdvancedConcurrencyController(2);
advancedController.addTask(createTask("高优先级", 500), 1);
advancedController.addTask(createTask("低优先级", 1000), 2);
- 错误重试
class RetryConcurrencyController extends ConcurrencyController {
constructor(maxConcurrency, maxRetries = 3) {
super(maxConcurrency);
this.maxRetries = maxRetries;
}
async _executeTaskWithRetry(task, resolve, reject) {
let retries = 0;
const execute = async () => {
try {
const result = await task();
resolve(result);
} catch (error) {
if (retries < this.maxRetries) {
retries++;
console.log(`任务重试中(第${retries}次)`);
await execute();
} else {
reject(error);
}
}
};
await execute();
}
_run() {
// 重写 _run 方法,替换任务执行逻辑
while (
this.runningCount < this.maxConcurrency &&
this.taskQueue.length > 0
) {
const { task, resolve, reject } = this.taskQueue.shift();
this.runningCount++;
this._executeTaskWithRetry(task, resolve, reject).finally(() => {
this.runningCount--;
this._run();
});
}
}
}
四、适用场景
场景 | 技术方案 | 说明 |
---|---|---|
分页数据预加载 | 基础并发控制(如并发数=3) | 同时加载多个分页数据,提升用户体验。 |
大文件分片上传 | 高级并发控制器 + 优先级 | 分片并行上传,优先传输关键分片。 |
批量图片懒加载 | 基础并发控制(如并发数=2) | 限制同时加载的图片数量,避免阻塞页面渲染。 |
实时数据轮询 | 动态优先级队列 | 确保重要数据的轮询请求优先执行。 |
接口请求失败重试 | 错误重试控制器 | 对失败请求自动重试,提高可靠性。 |