如何拆分Puppeteer异步方法,分批执行100页面自动化测试任务
分批处理Puppeteer任务,降低CPU负载
这个场景太常见了——一次性拉起100个页面执行异步操作,CPU肯定扛不住。咱们可以通过分批串行处理来解决,每次只启动指定数量的页面(比如你说的20个),等一批全部完成后再启动下一批,这样就能把资源占用控制在合理范围内。
先分析原代码的问题
你原来的代码用Object.keys(requirementsMap).forEach(async (key) => { ... }),然后await Promise.all(promises)——这会一次性把100个页面的创建、操作Promise全部启动,相当于同时打开100个页面,CPU直接拉满是必然的。
解决方案:分批处理
核心思路是:把所有任务分成小批次,用for循环(而不是forEach)控制批次的串行执行,每批内部并行处理20个任务,批与批之间等待完成。
下面是修改后的完整代码,我加了详细注释:
export default { async checkAllKeys() { const requirementsMap = { key_1: [this.method1, this.method2, this.method3], key_2: [this.method1, this.method2, this.method3], // ... 剩下的98个key key_100: [this.method1, this.method2, this.method3], }; const result = {}; const allKeys = Object.keys(requirementsMap); const batchSize = 20; // 每次处理20个页面,可根据机器性能调整 // 分批循环处理,for循环能保证批次串行执行 for (let i = 0; i < allKeys.length; i += batchSize) { // 截取当前批次的key const currentBatchKeys = allKeys.slice(i, i + batchSize); console.log(`开始处理第${Math.floor(i/batchSize)+1}批,共${currentBatchKeys.length}个任务`); // 处理当前批次的每个任务 const batchTasks = currentBatchKeys.map(async (key) => { let newPage; try { newPage = await browser.newPage(); await newPage.goto('http://www.example.com/'); const requirements = requirementsMap[key]; // 执行当前key的所有异步方法,捕获单个方法的错误 const keyResults = await Promise.all( requirements.map(req => req.call(this, newPage).catch(() => 'error occurred')) ); result[key] = keyResults; } catch (globalErr) { // 捕获页面创建、跳转等全局错误,避免影响整个批次 result[key] = ['页面操作全局错误']; console.error(`处理key ${key}时发生全局错误:`, globalErr); } finally { // 无论成功失败,都确保页面关闭,防止内存泄漏 if (newPage) await newPage.close(); } }); // 等待当前批次所有任务完成,再进入下一批 await Promise.all(batchTasks); // 可选:每批之间加短暂延迟,给CPU喘息时间 // await new Promise(resolve => setTimeout(resolve, 500)); } return result; }, async method1(newPage) { // 你的异步操作逻辑,比如点击、输入等 }, async method2(newPage) { // 注意:原代码里method2没传newPage,这里补上(如果需要用到页面的话) // 你的异步操作逻辑 }, async method3(newPage) { // 你的异步操作逻辑 }, };
关键改进点
- 串行批次,并行内部任务:用
for循环控制批次,每批完成后再处理下一批;每批内部用Promise.all并行处理20个页面,兼顾效率和资源占用。 - 简化Promise逻辑:去掉了原代码中不必要的
new Promise(async (resolve, reject))——async函数本身就返回Promise,直接用map生成任务Promise即可。 - 完善错误处理:增加
try/catch/finally,确保页面无论如何都会关闭,避免内存泄漏;同时捕获单个方法和全局操作的错误,不会因为一个任务失败导致整个批次挂掉。 - 可调试性:增加了批次处理日志,方便跟踪进度。
额外优化建议
- 如果你的机器性能还是有点吃紧,可以把
batchSize调小一点(比如15);如果性能足够,也可以调大。 - 可以在批次之间加短暂延迟(比如代码中注释的500ms),进一步降低CPU峰值。
内容的提问来源于stack exchange,提问作者domlas
相关产品推荐
相关产品推荐

