-1

Ok, so basically i'm creating an Interval class to handle repeating actions. I have something like this:

function Interval(fn, speed) {
	this.fn = fn;
	this.speed = speed;
	this.nt = setInterval(fn, speed);
}

And then i have 3 methods:

	this.pause = function() {
		clearInterval(this.nt);
	}

	this.start = function() {
		this.nt = setInterval(this.fn, this.speed);
		return this.nt;
	}

	this.wait = function(time) {
		this.pause();
		setTimeout(function() {
			this.start();
		}, time);
	}

The problem appears in the third method. this.pause(); and this.start(); works as expected. But when I nest this.start into a setTimeout function it stops working. I don't understand why. Here's an example:

var i = 0:
var nt = new Interval(function() {
    alert('Itineration: '+ i );
    if(i>5);
    nt.pause();
    setTimeout(nt.start, 2000);
    // nt.wait(2000);
  }, 500);

Neither nt.wait(2000); nor nt.pause(); setTimeout(nt.start, 2000); is working.

2
  • The answer below is correct, but in your code why do you have if(i>5); ? Commented Mar 18, 2015 at 5:01
  • This question has been asked about a dozen times. Search harder for an answer. Start off with stackoverflow.com/questions/2749244/…. Or, just debug your code. Place a breakpoint inside this.start, and examine the value of this. Voila. Commented Mar 18, 2015 at 5:18

3 Answers 3

4

this inside the timeout handler is not the Interval object, it is referring to the window object(not strict mode) so this.start() will not work

One solution is to pass a custom context using Function.bind()

this.wait = function (time) {
    this.pause();
    setTimeout(function () {
        this.start();
    }.bind(this), time);
    // setTimeout(this.start.bind(this), time) as @elclanrs suggested
}
Sign up to request clarification or add additional context in comments.

2 Comments

Why not setTimeout(this.start.bind(this), time)?
Would like to give -1 for failing to point out that this is a frequent duplicate and not pointing to a more authoritative answer.
0

You are running into a context issue with your code. When the setTimeout function executes your callback the definition of "this" is no longer your Interval object. You need to modify your code so that you maintain a proper reference to the Interval object.

this.wait = function(time) {
    var interval = this;
    interval.pause();
    setTimeout(function() {
        interval.start();
    }, time);
}

Edit

I just saw the other answer using .bind which is a much cleaner solution from a readability standpoint. One important note about .bind is that behind the scenes it basically generates another function to call your original function using the .call or .apply methods to set the correct value of this

In most cases the readability gained from using .bind is worth it. However, if this is going to be a core component to a larger system, it is a good idea to squeeze every ounce of performance you can out of it. Which would be an argument for avoiding .bind in this specific situation.

2 Comments

What you say is "one important note about .bind is not a note, it's actually the fundamental thing that bind does for a living. Your point about performance is quite misplaced. First, you have no way to know that calling bind is going to be faster or slower than writing out the anonymous function inline. Second, the probability that whatever difference there is is going to affect the performance of the app is vanishingly small.
Agree on both points. As a said in my answer, bind is a much more readable and pleasant solution. Again, to my point, it is more of a black box. The performance varies by browser and if you are trying to squeeze every ounce of performance, you might opt to not use it. See: stackoverflow.com/questions/8656106/…
0

Working example based on the other answers.

function Interval(fn, speed) {
    this.fn = fn;
    this.speed = speed;
    this.nt = setInterval(fn, speed);

    this.pause = function () {
        clearInterval(this.nt);
    }

    this.start = function () {
        this.nt = setInterval(this.fn, this.speed);
        return this.nt;
    }

    this.wait = function (time) {
        this.pause();
        setTimeout(function () {
            this.start();
        }.bind(this), time);
    }
}

var i = 0;
var nt = new Interval(function () {
    document.write('<pre>Itineration: ' + i + '</pre>');
    i++;
    nt.wait(2000);  
}, 500);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.