javascriptnode.jsnodemailernode-cronhtml-pdf

How to wait until a pdf is generated Javascript


I am using node html-pdf to generate a pdf and nodemailer to automatically send an email containing the pdf every minute using node-cron as demonstrated in the following code:

cron.schedule('* * * * *', () => {
    
    // function to generate scan report using html-pdf
    report.getScanReport();

    // e-mail message options
    let mailOptions = {
        from: {{some_email}},
        to: {{some_email}},
        subject: 'Testing',
        text: 'See PDF attachment',
        attachments: [{
            filename:'test.pdf',
            path: 'C:/Users/test.pdf',
            contentType: 'application/pdf'
        }]
    };

    // Send e-mail
    transporter.sendMail(mailOptions, function(error, info) {
        if (error) {
            console.log(error);
        } else {
            console.log('Email sent: ' + info.response);
        }
    });
})

The issue is that generating the pdf takes ~10 seconds or so, so the program tries to send the email before the generated pdf file exists and I then get the error: Error: ENOENT: no such file or directory

For reference here is how the getScanReport() function works:

const getScanReport = () => {
      Promise.all([
        pool.query(query1),
        pool.query(query2)
    ]).then(function([results1, results,]) {
        var obj1 = results1.rows;
        var obj2 = results2.rows;

        ejs.renderFile('./views/pages/scanreport.ejs', {scanObj: scanObj, woObj: woObj}, (err, data) => {
        if (err) {
            console.log(err);
        } else {
            let options = {
            "height": "11.25in",
            "width": "8.5in",
            "header": {
                "height": "20mm"
            },
            "footer": {
                "height": "20mm",
            },
            };
            let filename = "test.pdf";
            pdf.create(data).toFile(filename, function (err, data) {
            let fileloc = 'C:/Users/' + filename;
            if (err) {
                console.log(err);
            } else {
                console.log("Successfully created pdf");
            }
            })
        }
        });
    });
} 

Is there a way make sure the pdf is generated before trying to send the email?


Solution

  • Assuming you can modify your getScanReport function, use a Promise. Then in your email function, use async / await to wait for that promise.

    (You can click the links to read more about how promises and async / await work if you don't use them very often.)

    For example (edited since post now includes getScanReport inner workings):

    report.getScanReport = () => new Promise( 
    
        //announcePDFReady() is a function we call to resolve our promise
    
        announcePDFReady => {
    
            Promise.
                all( [ /* ... */ ] ).
                then( function( [ results1, results2 ] ) {
    
                    /* ... */
    
                    renderFile( /* ... */ ( err, data ) => {
    
                        /* ... */
    
                        pdf.create( data ).toFile( filename, ( err, data ) => {
                            
                            let fileloc = 'C:/Users/' + filename;
                            if (err) {
                                console.log(err);
                            } else {
                                console.log("Successfully created pdf");
                                //announce ready right here!
                                announcePDFReady( data );
                            }
    
                        } )
    
                    } )
    
                } )
                
        }
    
    );
    
    
    //...
    //we write 'async' before the cron job function definition
    async () => {
        //this is inside the cron job function
    
        const pdfIsReady = await report.getScanReport();
        //since report.getScanReport() returns a promise,
        //'await' here means our code will stop until the promise resolves.
    
    
        if( pdfIsReady === false ) {
            //it failed. Do something? Ignore it?
        } else {
    
            //pdf is ready. We can send the email
        
            //etc.
    
        }
    }