You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 16:06:05