Elle Nolan
Elle Nolan

Reputation: 399

I'm having a lot of trouble with setInterval and class methods

I keep running into bizarre problems. I've been unable to find anything on them after doing some research, so I thought I'd come here to present them. I have a class which is rather long, but I'll include the relevant bits:

class AnimatedSnake {

constructor(canvasId, coordinates) {

    this.coordinates = coordinates;
    this.direction = 2;

    this.ctx = document.getElementById(canvasId).getContext("2d");

    // 0 - .99, describes how far along snake is between coordinates
    this.progress = 0;
}

erase() {
    for (let i = 0; i < this.coordinates.length; i++) {
        let c1 = this.coordinates[i][0],
            c2 = this.coordinates[i][1];
        this.ctx.clearRect(c1 * 31, c2 * 31, 31, 31);
    }
}

next() {
    this.progress += 0.01;

    if (this.progress >= 1) {
        this.progress %= 1;

        let nextCoord = this.coordinates[4].slice();
        nextCoord[0] += ((this.direction % 2) * this.direction);
        nextCoord[1] += ((!(this.direction % 2) * (this.direction / 2)));
        this.coordinates.push(nextCoord);
        this.coordinates.shift();
    }
    console.log(this.erase);
    this.erase();
    this.draw();

}

}

So far, I can call AnimatedSnake.next() indefinitely if I'm doing it manually (i.e. from the console). However, when I put the function in an interval or timeout - setInterval(AnimatedSnake.next, 100) - it all of a sudden, on the first run, claims that AnimatedSnake.erase is not a function. I tried putting AnimatedSnake.erase() directly in the interval, and when I do THAT, for some absurd reason it goes and tells me that it cannot take the length property of AnimatedSnake.coordinates, which it claims is undefined. Nowhere in my code to I redefine any of these things. coordinates is altered, but it should not be undefined at any point. And erase is of course a method that I never change. Does anyone have any insight into why, when these are called with setInterval or setTimeout weird things happen, but if I call the functions repeatedly (even in a for loop) without the JavaScript timing functions everything works out fine? I'm genuinely stumped.

Upvotes: 1

Views: 71

Answers (1)

Ram
Ram

Reputation: 144679

Consider these two snippets:

animatedSnake.next()

And:

let method = animatedSnake.next;
method();

In the first snippet next is called as a member of animatedSnake object, so this within the context of next method refers to the animatedSnake object.

In the second snippet the next method is detached from the object, so this no longer refers to the animatedSnake instance when the method function is invoked. This is how passing a method to another function, like setInterval works. You can either use Function.prototype.bind method for setting the context manually:

setInterval(animatedSnake.next.bind(animatedSnake), 100)

or wrap the statement with another function:

setInterval(() => animatedSnake.next(), 100)

Upvotes: 3

Related Questions