Rodolfo BocaneGra
Rodolfo BocaneGra

Reputation: 357

How to synchronously send mails through nodemailer?

I'm creating an app using nodejs with the nodemailer module to send mails.

The process of my app is to read a list with names and emails, create png diploma files with jimp (based on each name and email) and store and send each one of them through nodemailer to each different mail addresses and after all this is done I want to delete each file but all this in a sync way, because the png diploma and sending the email takes some time:

The syntax of my list is:

const list = [
  [name1, [email protected]]
  [name2, [email protected]]
  [        ...           ]
  [namex, [email protected]]
]

Actually I want to wait for each mail to be sent because gmail seems to have a problem to handle sending multiple mails at time, after sending 13 or 15 mails it shows the next err:

error:  { Error: Data command failed: 421 4.7.0 Temporary System Problem.  Try again later (10). x12sm4645987otk.1 - gsmtp

So, in order to achieve this, I iterate over the list with a classic for loop (a foreach loop does it in an async way and doesn't let me to keep control over the diploma generation), I process each one of the positions of the

//Iterating over mails array
for (let i = 0; i < list.length; i++) {
    // Little msg to know what is going on
    console.log(`Processing address ${i} out of ${list.length}`)

    const element = list[i]
    // diplomaData is an object which contains data needed (such as name, course, etc) to create the diploma 
    diplomaData.name = element[0];
    // diplomaDir is the address in which each diploma gets stored, it is returned by the generateDiploma function  
    diplomaDir = await generator.generateDiploma(diplomaData)
    // So once the diploma is generated, I send its address to generateMailContentFunction
    // it returns an object which contains text like a greeting, congratulations and of course, the diploma address 
    mailContent = await mailer.generateMailContent(element, diplomaDir)
    // So the only thing pending is to send the email with the correspondent content
    await mailer.sendMail(mailContent)
    // I've commented this function because even it is declared in an async way it 
    // await utilities.remove(diplomaDir)
}

This is my sendMail function:

exports.sendMail = async (mailOptions) => {
    transporter.sendMail(mailOptions, (err, info) => {
        if (err) {
            console.log("error: ", err);
        } else {
            console.log(`Mail sent succesfully!`);
        }
    });
}

So in few words my problem is that nodemailer seems to launch all the mails at the same time after finishing the loop (I can confirm this because in my console the logs for "Processing address ..." appears before the ones from nodemailer, so I just want to make this process absolutely synchronous, could anybody help me please? :(

Upvotes: 3

Views: 4569

Answers (1)

FishSaidNo
FishSaidNo

Reputation: 405

Your sendMail function is not asynchronous in nature. It is kicking off an asynchronous function (ie. transporter.sendMail) then immediately returning undefined (as there is no return statement).

exports.sendMail = function(mailOptions){

   return new Promise(function (resolve, reject){
      transporter.sendMail(mailOptions, (err, info) => {
         if (err) {
            console.log("error: ", err);
            reject(err);
         } else {
            console.log(`Mail sent successfully!`);
            resolve(info);
         }
      });
   });

}

Now when you await mailer.sendMail(mailContent) a promise will be returned & there will actually be something to await. That is, the resolution or rejection of the promise.

Be sure to have a try/catch block enclosing any await operators.

Upvotes: 7

Related Questions