Async/await场景下MySQL回调中向Puppeteer传参异常问题求助
问题根因
- 异步变量闭包陷阱:你在
cluster.task的回调中直接引用了for循环内定义的dt、cpf变量,这些变量在每次循环时都会被覆盖,而异步任务的实际执行时机晚于for循环遍历完成的时间,因此所有任务拿到的都是最后一轮循环的变量值。 - Puppeteer-cluster使用逻辑错误:
cluster.task(任务定义)、cluster.idle()(等待所有任务完成)、cluster.close()(关闭集群)都被错误放在for循环内部,导致每遍历一条数据就重复定义任务、关闭集群,完全不符合集群批量处理的设计逻辑,还会进一步放大变量共享问题。
修复方案
调整代码逻辑,将集群相关的初始化、任务定义移到循环外,提交任务时把当前行的cpf、dt和url一起作为参数传入任务,避免引用外层循环变量,所有任务提交完成后再统一等待执行、关闭集群。
修正后代码如下:
const mysql = require('mysql'); const { Cluster } = require('puppeteer-cluster'); const sql = "select an.id_clientes, ano, c.nome, c.cpf, c.dt_nasc from clientes_anos an inner join clientes c on an.id_clientes = c.id limit 3"; db.query(sql, async (err, result) => { if (err) throw err; // 建议新增错误处理 // 集群初始化、全局配置移到循环外,仅执行一次 const cluster = await Cluster.launch({ concurrency: Cluster.CONCURRENCY_CONTEXT, maxConcurrency: 2, puppeteerOptions: { headless: false }, timeout: 90000, }); // 全局错误监听仅注册一次 cluster.on('taskerror', (err, data) => { console.log(`任务执行失败 ${data.url}: ${err.message}`); }); // 任务定义仅写一次,参数从传入的data中取,不引用外层变量 await cluster.task(async ({ page, data }) => { const { url, cpf, dt } = data; console.log('当前执行参数', dt, cpf); await page.goto(url); await page.type('#data_nascimento', dt); await page.type('input[name=CPF]', cpf); // 注:原逻辑中waitForNavigation位置有误,type操作不会触发跳转,若你后续有点击提交按钮的逻辑,需把该方法放在点击操作前后配合使用,避免超时 await page.waitForNavigation(); try{ const msg = await page.$eval( `#rfb-main-container table tbody tr:nth-of-type(1) td:nth-of-type(1)`, divs => divs.innerText.trim() ); if(msg.includes('Resultado encontrado: Saldo inexistente de imposto a pagar ou a restituir.')){ console.log('situação: '+ msg); }else{ console.log('0'); } }catch(exception){ console.log('Error', exception.message); } }); // 循环仅做参数处理、任务提交 for (const elem of result){ const cpf = elem.cpf; const dt = elem.dt_nasc.toLocaleDateString('pt-BR'); // 建议指定区域,避免不同环境日期格式不一致 const url = `https://servicos.receita.fazenda.gov.br/Servicos/ConsRest/Atual.app/paginas/index.asp?cpf=${cpf}&dtnasc=${dt}`; console.log('提交任务', url); // 提交任务时把三个参数一起传入 await cluster.queue({ url, cpf, dt }); } // 所有任务提交完成后,再等待全部执行完成、关闭集群 await cluster.idle(); await cluster.close(); });
内容的提问来源于stack exchange,提问作者brunob12
相关产品推荐
相关产品推荐

