这段代码实现了一个 并发控制的任务执行器 ,通过 runParallel
函数限制并发任务的数量,从而避免一次性执行过多的任务导致系统资源耗尽。以下是对代码逻辑的具体分析。
代码分析
1. 函数定义
bash
function runParallel(source, maxConcurrency, iteratorFn) {
-
参数说明:
source
:任务的来源,通常是一个数组或者可迭代对象,包含了所有需要执行的任务。maxConcurrency
:最大并发数,限制同时执行的任务数量。iteratorFn
:处理每个任务的函数,即每个任务的逻辑如何执行。
2. 初始化变量
ini
const ret = []; // 存储每个任务的返回值
const executing = []; // 当前正在执行的任务的 Promise 数组
ret
用于存储每个任务执行的结果。executing
用于跟踪当前正在执行的任务,方便控制并发数量。
3. 遍历任务
scss
for (const item of source) {
const p = Promise.resolve().then(() => iteratorFn(item));
ret.push(p);
-
核心逻辑:
- 遍历
source
中的每一个任务item
。 - 使用
Promise.resolve
确保iteratorFn(item)
的结果是一个 Promise,无论iteratorFn
是否同步。 - 将任务的 Promise 存储到
ret
中,最终会通过Promise.all(ret)
等待所有任务完成。
- 遍历
4. 并发控制
ini
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= maxConcurrency) {
await Promise.race(executing);
}
-
Promise 包装(
e
) :- 将当前任务
p
包装成一个新的 Promisee
,在p
完成后会将其从executing
中移除。 executing.splice(executing.indexOf(e), 1)
:从正在执行的任务数组中移除完成的任务。
- 将当前任务
-
控制并发数量:
-
每次将任务
e
添加到executing
中。 -
如果当前正在执行的任务数超过了
maxConcurrency
:- 使用
Promise.race(executing)
等待最快完成的任务结束,从而腾出并发资源。
- 使用
-
5. 返回结果
javascript
return Promise.all(ret);
Promise.all(ret)
等待所有任务完成,并返回所有任务的结果。
整体逻辑流程
- 遍历
source
中的任务。 - 为每个任务创建一个 Promise,并将其加入
ret
和executing
中。 - 如果
executing
的长度达到maxConcurrency
,等待任意一个任务完成后继续执行下一个任务。 - 当所有任务都被添加到
ret
中并执行完成后,通过Promise.all(ret)
返回所有任务的结果。
示例
假设有 5 个任务,每个任务需要 1 秒完成,并且设置最大并发数为 2:
javascript
const source = [1, 2, 3, 4, 5];
function iteratorFn(item) {
return new Promise((resolve) =>
setTimeout(() => {
console.log(`Task ${item} done`);
resolve(item);
}, 1000)
);
}
runParallel(source, 2, iteratorFn).then((results) => {
console.log("All tasks done:", results);
});
执行过程:
- 最开始启动任务 1 和任务 2。
- 任务 1 或 任务 2 完成后,任务 3 开始执行。
- 重复上述过程,直到所有任务完成。
输出结果:
bash
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
All tasks done: [1, 2, 3, 4, 5]
关键点
- 并发控制 :通过
Promise.race
实现对最大并发数的限制。 - 任务跟踪 :
executing
用于追踪当前正在执行的任务。 - 结果收集 :
ret
用于收集所有任务的结果,最终通过Promise.all
返回。
这段代码是典型的 并发控制模板,适用于需要在有限资源下执行大量异步任务的场景,比如批量请求、爬虫等。