0

I'm trying to define a Javascript class with a repeating function but I can't get it to work:

var Repeater = function() {
    this.init.apply(this, arguments);
};

Repeater.prototype = {
    run: 0, // how many runs
    interval: 5, // seconds

    init: function() {
        this.repeat();
    },

    repeat: function() {
        console.log(++this.run);
        setTimeout(this.repeat, this.interval * 1000);
    }
};

var repeater = new Repeater();

How should this be done?

Vlad
  • 9,526
  • 2
  • 30
  • 37
  • 2
    Do you really mean to have all instances share `run` and `interval`? – Ian Aug 15 '13 at 15:13
  • @Ian I'm using it as a singleton, but your point is valid – Vlad Aug 15 '13 at 15:17
  • 1
    If it's meant as a singleton, why are you using a constructor approach? I feel like a simple function would suffice – Ian Aug 15 '13 at 15:21
  • If it matters, here's how I'd set it up: http://jsfiddle.net/BcaYy/ . I'm obviously not sure how this is being used, but that's how I interpreted it – Ian Aug 15 '13 at 15:25
  • @Ian thank you, this was just an example; in reality I have a lot of other stuff going on, which I left out for the sake of simplicity. – Vlad Aug 15 '13 at 15:34

3 Answers3

2

Try this code:

var Repeater = function() {
    this.run = 0;  // how many runs
    this.interval = 5; // seconds
    this.init.apply(this, arguments);
};

Repeater.prototype.init = function() {
    this.repeat();
}

Repeater.prototype.repeat = function() {
    var _this = this;
    console.log(++this.run);
    setTimeout(function () { _this.repeat() }, this.interval * 1000);
};

var repeater = new Repeater();

I've moved run and interval into constructor, because if you add this to prototype then this will be spread over all instances.

Your problem lies into seTimeout - in your code this timer set new scope for repeater and this was no longer pointing to Repeater instance but for Timeout instance. You need to cache this (I've called this cache _this) and call it into new function passed to setTimeout.

codename-
  • 12,008
  • 2
  • 24
  • 29
1

Try like that:

var Repeater = function() {
    this.init.apply(this, arguments);
};

Repeater.prototype = {
    run: 0, // how many runs
    interval: 5, // seconds

    init: function() {
        this.repeat();
    },

    repeat: function() {
        console.log(++this.run);
        var that = this;
        setTimeout(function() {that.repeat()}, this.interval * 1000);
    }
};

var repeater = new Repeater();

You can read more on how this behaves in this question : How does the "this" keyword work?

Community
  • 1
  • 1
lpiepiora
  • 13,246
  • 1
  • 33
  • 45
  • How is that any different than what the OP has done? (you're missing parentheses after that.repeat() ) – Adam Aug 15 '13 at 15:10
  • 1
    @Adam That value of `this` inside of `setTimeout` is not what you expect. Setting `var that = this;` before it and using that will ensure it's referring to the right object. – Ian Aug 15 '13 at 15:11
  • 1
    I see that it works, but I think you should elaborate your answer. this.repeat inside setTimeout does indeed call the correct function, but it does not call it with the correct scope. setTimeout executes it's function parameter in the global scope, which is what was happening here. Thanks for helping me understand this! – Adam Aug 15 '13 at 15:15
  • `but I think you should elaborate your answer` - I completely agree. Code-only answers don't really help – Ian Aug 15 '13 at 15:17
  • 1
    I've added a link to another question explaining it, because I feel `this` is widely misunderstood, and actually I would duplicate answers given in that other question. Otherwise I agree with both of you - I should have elaborated in the first place ;) – lpiepiora Aug 15 '13 at 15:19
0

Change your repeat function to use a closure in the setTimeout call like so:

repeat: function() {
var ctx = this;
    console.log(++this.run);
    setTimeout(function(){ctx.repeat()}, this.interval * 1000);
}

You need to set the context explicitly in these kinds of scenarios- that's what the ctx variable is for

CodeOwl
  • 624
  • 8
  • 21