如何在Node.js中运行并行进程并限制同时运行的进程数量?
控制Node.js子进程并发数的实现方案
这问题我之前做批量任务的时候也遇到过,核心就是要控制并发进程数,保持同时运行的进程不超过5个,而且只要有一个进程结束,就立刻启动下一个任务,直到所有200个文件都处理完。下面给你两种实用的实现方式:
方法一:原生Promise手动控制(无额外依赖)
这种方式不需要安装任何第三方包,用Node.js原生的Promise和递归逻辑就能实现并发控制:
const { exec } = require('child_process'); // 你的200个文件数组 const files = ['word.doc', 'foo.pdf', 'a.txt', /* ... 其他文件 */]; const maxConcurrent = 5; // 最大并发数 let currentTaskIndex = 0; let runningProcesses = 0; // 封装单个文件的执行逻辑,返回Promise function executeWork(file) { return new Promise((resolve, reject) => { // 注意给文件名加双引号,避免带空格的文件名解析错误 const process = exec(`Work.exe "${file}"`, (error, stdout, stderr) => { if (error) { console.error(`处理文件 ${file} 出错:`, error.message); reject(error); return; } console.log(`文件 ${file} 处理完成`); resolve(stdout); }); // 监听进程退出事件,确保无论正常结束还是异常退出都能捕获 process.on('exit', (exitCode) => { if (exitCode !== 0) { reject(new Error(`文件 ${file} 进程退出码: ${exitCode}`)); } }); }); } // 启动下一个任务的递归函数 function startNextTask() { // 所有任务都已启动,直接返回 if (currentTaskIndex >= files.length) return; const currentFile = files[currentTaskIndex]; currentTaskIndex++; runningProcesses++; executeWork(currentFile) .finally(() => { // 无论任务成功失败,都减少运行计数并启动下一个任务 runningProcesses--; startNextTask(); }); } // 初始化启动maxConcurrent个任务 for (let i = 0; i < maxConcurrent; i++) { startNextTask(); }
代码说明:
executeWork:把child_process.exec的回调风格API包装成Promise,方便用异步逻辑控制流程startNextTask:负责启动单个任务,任务结束后(不管成功还是失败)都会递归调用自己,启动下一个任务- 初始化阶段先启动5个任务,之后就靠每个任务结束后的
finally回调自动补充新任务,始终保持最多5个进程在运行
方法二:使用async库的队列(更简洁)
如果愿意引入第三方依赖,async库的queue方法专门为这种并发队列场景设计,代码会更简洁易读:
首先安装依赖:
npm install async
然后实现代码:
const { exec } = require('child_process'); const async = require('async'); const files = ['word.doc', 'foo.pdf', 'a.txt', /* ... 其他文件 */]; // 创建并发队列,设置最大并发数为5 const taskQueue = async.queue((file, taskCallback) => { // 执行单个文件的处理逻辑 exec(`Work.exe "${file}"`, (error, stdout, stderr) => { if (error) { console.error(`处理文件 ${file} 出错:`, error.message); taskCallback(error); return; } console.log(`文件 ${file} 处理完成`); taskCallback(null, stdout); }); }, 5); // 监听队列全部完成事件 taskQueue.drain(() => { console.log('所有文件都已处理完成!'); }); // 将所有文件加入队列 taskQueue.push(files);
代码说明:
async.queue:第一个参数是单个任务的处理函数,第二个参数是最大并发数taskQueue.push:把所有任务加入队列后,队列会自动控制并发数,一个任务完成就立刻启动下一个drain事件:当队列里所有任务都处理完成时触发,适合做后续的收尾工作
额外注意点:
- 文件名空格处理:一定要给文件名加上双引号(
"${file}"),避免文件名包含空格时命令解析出错 - 错误处理:两种方案都包含了错误捕获,你可以根据需求调整错误处理逻辑(比如某个任务失败是否终止所有任务)
- 结果收集:如果需要收集所有任务的处理结果,可以在Promise版本里用数组存储resolve的值,或者在
async队列的push方法里添加回调来收集
内容的提问来源于stack exchange,提问作者raxinaga
相关产品推荐
相关产品推荐

