Nodejs - How to use external variables in asynchronous functions

Asked

Viewed 408 times

3

I’m starting to node and I still find the concept of asynchronous functions complicated. I am using and js to render templates to e-mail in my application.

The prototype is:

"use strict";
const nodemailer = require('nodemailer');
const path = require('path');
const ejs = require('ejs');

function EmailManager(){
  this.configs ={};
  this.receivers = "";
  this.subject = "";
  this.template="";
  this.context ={};

};

EmailManager.prototype.send = function(){

    ejs.renderFile(path.join(__dirname,'..','templates',this.template),this.context, function(err, data) {

        if(err){
                 throw err;
        }

        var transporter = nodemailer.createTransport(this.configs);

        var mailOptions = {
                from: '"'+ this.configs.sender 
                + ' <'+ this.configs.user +'>', // sender address
                to: this.receivers, // list of receivers
                subject: this.subject, 
              }; 

        mailOptions.html = data;


        transporter.sendMail(mailOptions, function(error, info){
              if(error){
                 throw error;
              }
              //console.log('Message sent to '+ this.receivers+", subject: "+ this.subject);
        });

     });

};

To run, I create a new instance, configure all internal variables and run send() then. However, when executed the following error message appears:

error typeerror: Cannot read Property 'configs' of Undefined (Ode:9820) Unhandledpromiserejectionwarning: Unhandled Promise rejection (rejection id: 4): Typeerror: Cannot read Property 'configs' of Undefined

It seems the reference this is lost within asynchronous function, I read that type functions disregard the external context. So how could I pass this reference to my method? I also had this same problem with other similar functions.

1 answer

2

As you suggested the execution context (the this) is not what is expected. There are some tools to fix this. The two places where this can happen is in the function itself .send() and then into the callback of the method ejs.renderFile.

Assuming that the context of this within the .send() is correct (if you don’t have to show how this code is going), you can do it in two ways to force the this at the instance of EmailManager inside the callback:

Using .bind():

ejs.renderFile(path.join(__dirname, '..', 'templates', this.template), this.context, function(err, data) {
    // ... o código dentro da callback
}.bind(this)); // <--- usando ".bind()"

Using self, a reference to the this:

You can create a variable with the name self to be a reference, pointer, to the instance of EmailManager. Then it would be so:

EmailManager.prototype.send = function() {
    var self = this; // <-- agora podes usar o "self" em vêz do "this"
    
    ejs.renderFile(path.join(__dirname, '..', 'templates', self.template), self.context, function(err, data) {
        if (err) throw err;

        var transporter = nodemailer.createTransport(self.configs);
        var mailOptions = {
            from: '"' + self.configs.sender + ' <' + self.configs.user + '>', // sender address
            to: self.receivers, // list of receivers
            subject: self.subject,
        };
        mailOptions.html = data;
        transporter.sendMail(mailOptions, function(error, info) {
            if (error) throw error;
            console.log('Message sent to '+ self.receivers+", subject: "+ self.subject);
        });
    });
};
  • making use of Arrow Function there would not give the right context to the function? (I wonder why it is using ES6 and Nodejs)

  • @Moshmage yes maybe it is an option. But since AP does not use ES6 classes I deduced that it has an old Node version, pre ES6. Looking better I see use of const... but it depends on what the code is called.

  • As I saw there const at first, I was in doubt

  • @Moshmage is vi now too, in time to edit my comment. It would be interesting to see how EmailManager.prototype.send is called to be sure.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.