Malvinka
Malvinka

Reputation: 1379

setInterval calling class method

I cannot make the timer to call my class method. This works fine:

MyInput = class {
 constructor(params) {
  let input = params.input;
  input.addEventListener('focus', this.setTimer);
 }

 setTimer() {
  this.timer = window.setInterval(() => {
   console.log('yay');
  }, 300);
 }
};

but this:

MyInput = class {
 constructor(params) {
  let input = params.input;
  input.addEventListener('focus', this.setTimer);
 }

 setTimer() {
  this.timer = window.setInterval(this.lookupOptions, 300);
 }

 lookupOptions() {
  console.log('yay');
 }
};

doesn't call the lookupOptions method but instead, my developer tools in the browser stops debugger every 300ms (checked with different values - always in synch with the timer). After a while, it opens some strange file VM[number] with different numbers. I have no idea why it doesn't work. When I used timer outside the class in the same way it worked fine it seems like some problem with calling the class method but I cannot figure out what the problem might be. Could you please help?

Upvotes: 0

Views: 59

Answers (1)

Emiel Zuurbier
Emiel Zuurbier

Reputation: 20934

When setInterval calls the function it changes the context of this to the scope of the setInterval, which would be the window object.

Scope the callback in setInterval by wrapping it with an arrow function expression. This prevents the scope from being changed.

MyInput = class {
    constructor(params) {
        let input = params.input;
        input.addEventListener("focus", () => {
            this.setTimer();
        });
    }

    setTimer() {
        this.timer = window.setInterval(() => {
            this.lookupOptions();
        }, 300);
    }
  
    lookupOptions() {
         console.log("yay");
    }
}

Or use the .bind() method, that is present on each function object to manually set the scope for this.

MyInput = class {
    constructor(params) {
        let input = params.input;
        const boundSetTimer = this.setTimer.bind(this)
        input.addEventListener("focus", boundSetTimer);
    }

    setTimer() {
        const boundLookupOptions = this.lookupOptions.bind(this)
        this.timer = window.setInterval(boundLookupOptions, 300);
    }
  
    lookupOptions() {
         console.log("yay");
    }
}

Or use the experimental public fields, which do somewhat the same as arrow function expressions, but as a method of an object.

MyInput = class {
    constructor(params) {
        let input = params.input;
        input.addEventListener("focus", this.setTimer);
    }

    setTimer = () => {
        this.timer = window.setInterval(this.lookupOptions, 300);
    }
  
    lookupOptions = () => {
         console.log("yay");
    }
}

Upvotes: 2

Related Questions