const pdf = require('pdfkit')
const QR = require('qrcode')
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}
The problem seems to be that async/await
isn't working properly for the createTicketPDF
function.
When I run the above emailTickets
function a blank pdf document is emailed. However, when I run it with the below setTimeOut
, the pdf contains all the detail that I want.
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
setTimeout(async() => {await sendGridEmail(emailDetails, true)}, 5000);
}
I would prefer a solution where the code waited for the createTicketPDF
function to finish before calling the sendGridEmail
function.
Below is the code for the createTicketPDF
function:
const createTicketPDF = async (tickets, userEvent) => {
const doc = new pdf
doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
await tickets.map(async(ticket, index)=> {
return(
await doc.fontSize(30),
await doc.text(userEvent.title),
await doc.fontSize(10),
await doc.moveDown(),
await doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
await doc.moveDown(),
await doc.fontSize(20),
await doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
await doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
await doc.addPage()
)
})
doc.end()
}
I had a forEach
loop in this function but replaced it with a map on learning that forEach
loops don't work well with async await
.
I have also tried using (const ticket of tickets){}
but this didn't work either
VS code is suggesting that none of the await
s within the map do anything
For completeness, below is the createQRCodes
. This function is working perfectly.
const createQRCodes = async (tickets) => {
let CreateQRCodes = tickets.map(ticket => {
return(
QR.toFile(`./qrCodes/${ticket._id}.png`, String([ticket._id, ticket.randomNumber, ticket.creationTime.getTime(), ticket.userEvent]))
)})
await Promise.all(CreateQRCodes)
}
Any ideas where I am going wrong
Twilio SendGrid developer evangelist here.
As far as I can tell from the documentation, PDFKit is a synchronous library. The only thing that is asynchronous is writing the PDF to disk. So your code should not need to await
anything within the createTicketPDF
function. You do need to listen for the stream to finish though, so you could return a promise that resolves when the stream is finished, like this:
const createTicketPDF = (tickets, userEvent) => {
const doc = new pdf
const stream = doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
tickets.map((ticket, index)=> {
return(
doc.fontSize(30),
doc.text(userEvent.title),
doc.fontSize(10),
doc.moveDown(),
doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
doc.moveDown(),
doc.fontSize(20),
doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
doc.addPage()
)
})
doc.end()
const promise = new Promise((resolve) => {
stream.on("finish", () => {
resolve();
})
});
return promise;
}
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}