JavaScript如何确保PDF生成完成后再执行邮件发送操作
解决方案
问题根源
你当前的问题本质是异步流程没有做等待控制:getScanReport内部包含数据库查询、ejs渲染、PDF生成三个异步操作,都是通过回调执行,函数本身没有返回异步完成的信号,所以定时任务调用getScanReport后会直接执行后续的邮件发送逻辑,此时PDF还没生成完毕,自然会报文件不存在的错误。
步骤1:改造getScanReport方法,将回调逻辑封装为Promise
我们把所有回调式的异步调用都包装为Promise,让函数可以返回异步执行结果,支持await等待:
const { promisify } = require('util'); // 将ejs的回调式渲染方法转为返回Promise的方法 const renderFile = promisify(ejs.renderFile); // 封装PDF生成的回调逻辑为Promise const pdfCreateToFile = (pdfInstance, filename) => { return new Promise((resolve, reject) => { pdfInstance.toFile(filename, (err, data) => { if (err) return reject(err); const fileloc = 'C:/Users/' + filename; resolve(fileloc); }) }) } const getScanReport = async () => { // 等待两个数据库查询完成 const [results1, results2] = await Promise.all([ pool.query(query1), pool.query(query2) ]); // 这里修正了你原代码的变量名笔误:你之前定义了obj1、obj2,传参却用了scanObj、woObj const scanObj = results1.rows; const woObj = results2.rows; // 等待ejs模板渲染完成 const html = await renderFile('./views/pages/scanreport.ejs', {scanObj, woObj}); const options = { "height": "11.25in", "width": "8.5in", "header": { "height": "20mm" }, "footer": { "height": "20mm", }, }; const filename = "test.pdf"; const pdfInstance = pdf.create(html, options); // 等待PDF文件写入磁盘完成,返回文件路径 const filePath = await pdfCreateToFile(pdfInstance, filename); console.log("Successfully created pdf"); return filePath; }
步骤2:修改定时任务逻辑,用async/await等待PDF生成完成
将定时任务的回调改为async函数,等待getScanReport执行完成后再触发邮件发送:
cron.schedule('* * * * *', async () => { try { // 等待PDF完全生成,拿到生成后的文件路径 const pdfPath = await report.getScanReport(); // 构造邮件参数 let mailOptions = { from: {{some_email}}, to: {{some_email}}, subject: 'Testing', text: 'See PDF attachment', attachments: [{ filename:'test.pdf', path: pdfPath, contentType: 'application/pdf' }] }; // 发送邮件 transporter.sendMail(mailOptions, function(error, info) { if (error) { console.log('邮件发送失败:', error); } else { console.log('Email sent: ' + info.response); } }); } catch (err) { // 统一捕获全流程错误:数据库查询/模板渲染/PDF生成失败都会在这里触发 console.log('任务执行出错:', err); } })
补充说明
如果不想改动太多原有代码,也可以选择将邮件发送逻辑封装为函数,传入getScanReport的toFile成功回调里触发,但这种写法会导致逻辑耦合,可维护性远低于上述Promise+async/await的方案。
内容的提问来源于stack exchange,提问作者NicLovin
相关产品推荐
相关产品推荐

