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.
- using closure
bind(this);
- Immediately executing function expression
But first let’s 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 IEFE and 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
.