64

I am trying to use setTimeout() inside a class function in JavaScript. The setTimeout() is supposed to trigger another method in the same Class, so the function I am passing it is written as window.setTimeout("this.anotherMethod", 4000). That bring the problem: this references the calling Object, in the case of setTimeout() it is window. How can I use enclosures to return a reference to the Class Object itself?

myObject = function(){

this.move = function(){
    alert(this + " is running");
}
this.turn = function(){
    alert(this + " is turning");
}
this.wait = function(){
    window.setTimeout("this.run" ,(1000 * randomNumber(1,5)));
}

this.run = function(){
    switch(randomNumber(0,2)){
        case 0:
            this.move();
        break;
        case 1:
            this.turn();
        break;
        case 2:
            this.wait();
    }
}

}

Dean
  • 7,814
  • 8
  • 30
  • 31

14 Answers14

103

You can do this:

 var that = this;
 setTimeout(function () {
     that.doStuff();
 }, 4000);

You can also bind for more succinct code (as originally pointed out by @Raynos):

setTimeout(this.doStuff.bind(this), 4000);

bind is a standard library function for exactly this coding pattern (ie capturing this lexically).

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
13

You can also bind a function to scope.

setTimeout(this.run.bind(this) ,(1000 * randomNumber(1,5)));

Be warned Function.prototype.bind is ES5

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 2
    you can just get bind to work on any js project: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind :) – Paul Scheltema Oct 01 '13 at 22:41
8

this can be problematic in javascript, as you've discovered.

I usually work around this by aliasing this inside the object so that I can use the alias whenever I need a reference back to the containing object.

MyObject = function ()
{
    var self = this;

    // The rest of the code goes here

    self.wait = function(){
        window.setTimeout(self.run ,(1000 * randomNumber(1,5)));
    }
}
GordonM
  • 31,179
  • 15
  • 87
  • 129
  • I find `self` to be a most misleading identifier, not least because it has very similar connotations to `this` which, as you point out, means something else. – Lightness Races in Orbit May 06 '11 at 12:31
  • 1
    Well it is just an example, you could call it MyObjRef, or anything else you think it should be. The point is that it gives you a way of getting the object reference. – GordonM May 06 '11 at 12:56
5
class A{

   setTimeout(()=>{

       // here this != undefined because of arrow function

  },500);

}
ChrisMM
  • 8,448
  • 13
  • 29
  • 48
  • 9
    When answering an old question, your answer would be much more useful to other StackOverflow users if you included some context to explain how your answer helps, particularly for a question that already has an accepted answer. See: [How do I write a good answer](https://stackoverflow.com/help/how-to-answer). – David Buck Jan 31 '20 at 14:40
4
this.wait = function(){
    var self = this;
    window.setTimeout(function() { self.run() } ,(1000 * randomNumber(1,5)));
}

So you store the reference to the object you're calling .run on in a local variable ('self').

Gijs
  • 5,201
  • 1
  • 27
  • 42
3

this is sensitive to the context in which it is called. When you pass a string to setTimeout then that is evaled in a completely different context.

You need to preserve the current value of this (by copying it to a different variable) and maintain the scope (by not using (implied) eval).

this.wait = function(){
    var self = this;
    setTimeout(function () { self.run() },
              (1000 * randomNumber(1,5))
              );
}
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
3

At the top of your main myObject make a new reference to the current value of this:

var self = this;

and then create a closure for your timer callback that uses that new reference instead of the global object that setTimeout will use as the default context in callbacks:

setTimeout(function() {
    self.run();
}, 4000);
Alnitak
  • 334,560
  • 70
  • 407
  • 495
2

you can just use the arrow function syntax:

setTimeout(() => {
     this.doStuff();
 }, 4000);
2
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

inside func, this always refer to the global object. you can pass in the current object into func,

var timeoutID = window.setTimeout(func, delay, this);
function func(that) {...}

unfortunately it does NOT work in IE

Note that passing additional parameters to the function in the first syntax does not work in Internet Explorer.

nandin
  • 2,549
  • 5
  • 23
  • 27
1

Have you tried;

window.setTimeout("myObject.run" ,(1000 * randomNumber(1,5)));
BusterLuke
  • 298
  • 2
  • 4
  • 11
0

Shorter way. Without anonymous func.

    var self = this;
    setTimeout(self.method, 1000);
Track83
  • 59
  • 4
0

It is not recommended to use setTimeout or setInterval using strings

setTimeout("myFunction()", 5000);

//this is the same as 

setTimeout(function(){ eval("myFunction()"); }, 5000)); //<-- eval == BAD
Vitim.us
  • 20,746
  • 15
  • 92
  • 109
0

Ran into a more complex situation...class A has a member of type B and a method that calls setTimeout which calls a method on class B. Solved as follows:

class A {
    constructor(b) {
        this.b = b;
    }
    setTimer(interval) {
        setTimeout(this.b.tick.bind(this.b), interval);
    }
}
class B {
    constructor(name){
        this.name = name;
        this.ele = window.document.getElementById('B');
    }
    tick() {
        console.log(this);
        this.ele.innerText += ' ' + this.name;
    }
}

Which bound A.b to this within B.tick and worked.

Here's a fiddle with bind: https://jsfiddle.net/jrme9hyh/

And one without bind which fails: https://jsfiddle.net/2jde8tq3/

philn5d
  • 636
  • 7
  • 12
0

You can use this code instead, which works in all modern browsers -

setTimeout(function(thisObj) {thisObj.run();},1000,this);

Ref: http://klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/

KalEl
  • 8,978
  • 13
  • 47
  • 56