Nodejs - How to use external variables in asynchronous functions

3

I'm starting in node and I still find the concept of asynchronous functions complicated. I'm using js to render templates to email 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, set all internal variables, and then run send() . However, when I run the following error message appears:

  

TypeError error: Can not read property 'configs' of undefined   (node: 9820) UnhandledPromiseRejectionWarning: Unhandled promise   rejection (rejection id: 4): TypeError: Can not read property 'configs'   of undefined

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

    
asked by anonymous 04.12.2016 / 23:55

1 answer

2

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

Assuming that the context of this within .send() is correct (if you do not have to show how you're running this code), you can do two ways to force this to EmailManager within 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 of this :

You can create a variable with the name self to be a reference, pointer, of the instance of EmailManager . In this case it would look like this:

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);
        });
    });
};
    
05.12.2016 / 10:15