setTimeout with reference to object

The problem

Calling setTimeout or setInterval from an object can be sometimes problematic. The timers are executed outside of your object’s context. You loose reference to your object and can only work with global variables. this suddenly points to window instead.

Solution

I’ve found out that there are plenty of ways to achieve it only after I published this post online. You can use whichever you suits your needs.

  1. using closure
  2. bind(this);
  3. Immediately executing function expression

But first lets see what we’re working with. For all three methods we’ll use the same object, only one function will look different. We’re creating new object of type Person with name Steven and make him say his name after the delay using setTimeout.

// Create new 'class'
function Person(name_)
{
  this.name = name_;
}

// Say whats my name after 2 seconds
// we plug in the reference to `this` person
// with variable named `self`
Person.prototype.whatsMyName = function()
{
  // This one will fail silently with an empty log
  setTimeout(function(){
    console.log(this.name)
  }, 2000);
}

// Define Steven and call him!
var stev = new Person('Steven');
stev.whatsMyName();


Closure

The simplest method will fit in many cases. All variables that are defined in the same block with setTimeout will be available. We could provide the whole context of current object by defining new variable like so:

Person.prototype.whatsMyName = function()
{
  var self = this;
  setTimeout(function(){
    console.log(self.name);
  }, 2000);
}

bind(this);

More elegant method. Here we use bind() to bind the object’s context to the scheduled function.

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

So after 2 seconds, calling this will actually refer to our object. You don’t have to define new variable.

Person.prototype.whatsMyName = function()
{
  setTimeout(function(){
    console.log(this.name)
  }.bind(this), 2000);
}

Immediately executing function expression (IEFE)

May also be known as Self-executing anonymous function.

Person.prototype.whatsMyName = function()
{
  setTimeout((function(param){
    return function() {
      console.log(param);
    }
  })(this), 2000)
}

setTimeout is invoked with 2 parameters, first being IEFEand second, the 2 seconds delay. The function itself also accepts one argument param. This argument can be for example a reference to the object we’re trying to call after 2 seconds, in this case this.

  • husa

    or just use Function.prototype.bind
    setTimeout(function(param) {
    console.log(param);
    }.bind(null, param), 2000);

    • True, someone else already pointed that out on G+.
      But like this:

      setTimeout(function() {
      console.log(this.name);
      }.bind(this), 2000);

      I should re-write this post 🙂

      • husa

        Yeah, actually you can use bind not only to bind context, but also parameters

  • Chris West

    Besides using bind as suggested, scope isn’t lost as much as context is. Take this for example:

    var a = 2;
    (function() {
    var a = 1;
    setTimeout(function() { console.log(a); }, 100);
    })();

    Executing this will print “1” in the console because the scope is still accessible within the timeout. Of course, if you are trying to access the “this” object you would need to alias it as something else like “self”.

    • I have completely re-written the whole article including suggestions from guys at G+ and the one you provided. I don’t want to have misleading articles here 🙂 Thanks!