Cannot call function using this in class

Viewed 329

I have a class called Scheduler which execute a cron job using the module cron. I have created a function to get the days difference between two dates, that function is working if I call it outside the cron job iteration otherwise it will return

TypeError: this.getDaysDifference is not a function

here's my code:

const CronJob = require('cron').CronJob;

class Scheduler {

    async start() {

        // HERE WORKING
        console.log(this.getDaysDifference(new Date('2020-03-29'), new Date('2020-03-30')));

        const job = new CronJob('0 */1 * * * *', async function () {
            let messages = await MessageModel.find();
            for (const msg of messages) {
                // HERE NOT WORKING
                console.log(this.getDaysDifference(new Date(), msg.lastScheduler));
            }
        });

        job.start();
    }

    getDaysDifference = function(start, end) {
        const _MS_PER_DAY = 1000 * 60 * 60 * 24;
        const utc1 = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
        const utc2 = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
        return Math.floor((utc2 - utc1) / _MS_PER_DAY);
    }
}

exports.Scheduler = Scheduler;
2 Answers

The this.getDaysDifference is not pointing to the instance of the class Scheduler in your callback since you have used a function.

There are two ways to fix this:

  • Use an arrow function () => {} which is very straight forward.

  • Bind the this to your instance explicitly using a functionObj.bind(yourInstance).

You can use an arrow function that will bind this to the lexical this in your callback definition:

new CronJob('0 */1 * * * *', async () => {
     let messages = await MessageModel.find();
     for (const msg of messages) {
         //this will be the lexical  this i.e. point to the instance o sthe Scheduler class
         console.log(this.getDaysDifference(new Date(), msg.lastScheduler));
        }
});

Solution using bind, where you explicitly bind the value of this to the instance of your class:

let cronJobCallback = async function () {
      let messages = await MessageModel.find();
      for (const msg of messages) {
      // HERE NOT WORKING
      console.log(that.getDaysDifference(new Date(), msg.lastScheduler));
      }
 }
 cronJobCallback = cronJobCallback.bind(this);
 new CronJob('0 */1 * * * *', cronJobCallback);

When the callback given to CronJob is executed it will be in another context and thus this will not refer to your Scheduler instance (maybe to the CronJob instance). Try capturing this before making your `CronJob´ sintance:

    async start() {

        // HERE WORKING
        console.log(this.getDaysDifference(new Date('2020-03-29'), new Date('2020-03-30')));
        const that = this;
        const job = new CronJob('0 */1 * * * *', async function () {
            let messages = await MessageModel.find();
            for (const msg of messages) {
                // HERE NOT WORKING
                console.log(that.getDaysDifference(new Date(), msg.lastScheduler));
            }
        });

        job.start();
    }

Alternative, use an arrow function.

Related