Passing objects to setTimeout and setInterval timed functions
Learn how to properly pass objects and maintain context when using JavaScript's timing functions
Passing objects to setTimeout and setInterval timed functions
For JavaScript timed events, we always rely on setTimeout and setInterval to execute a bit of code after a time period or at regular intervals. They also have a very simple syntax. Generally, the code to execute is run inside an anonymous function.
The Basic Usage
Here's the standard way we use these functions:
// Basic setTimeout
setTimeout(function() {
console.log('This runs after 1 second');
}, 1000);
// Basic setInterval
setInterval(function() {
console.log('This runs every 2 seconds');
}, 2000);
The Context Problem
The challenge arises when you need to access object properties or methods from within these timed functions. The this
context inside the callback function is not what you might expect.
Consider this example:
function MyObject() {
this.value = 42;
setTimeout(function() {
console.log(this.value); // undefined
}, 1000);
}
const obj = new MyObject();
The this.value
inside the setTimeout callback will be undefined
because this
no longer refers to the MyObject
instance. Instead, it refers to the global object (or is undefined in strict mode).
Solution 1: Capturing the context in a variable
A common solution is to capture the context in a variable before the timed function:
function MyObject() {
this.value = 42;
const self = this; // Capture the context
setTimeout(function() {
console.log(self.value); // 42
}, 1000);
}
const obj = new MyObject();
Solution 2: Using bind()
Another approach is to use the bind()
method to explicitly set the function's context:
function MyObject() {
this.value = 42;
setTimeout(function() {
console.log(this.value); // 42
}.bind(this), 1000);
}
const obj = new MyObject();
Solution 3: Arrow functions (ES6)
If you're using ES6, arrow functions automatically inherit the this
value from the enclosing context:
function MyObject() {
this.value = 42;
setTimeout(() => {
console.log(this.value); // 42
}, 1000);
}
const obj = new MyObject();
Passing Additional Parameters
Both setTimeout
and setInterval
accept additional parameters that will be passed to the callback function:
function greet(name, message) {
console.log(`${message}, ${name}!`);
}
// This passes "John" and "Hello" to the greet function
setTimeout(greet, 1000, "John", "Hello");
Practical Example
Here's a more practical example of a UI component that uses setInterval while properly maintaining context:
class Countdown {
constructor(element, seconds) {
this.element = element;
this.seconds = seconds;
this.interval = null;
}
start() {
this.updateDisplay();
this.interval = setInterval(() => {
this.seconds--;
this.updateDisplay();
if (this.seconds <= 0) {
this.stop();
}
}, 1000);
}
stop() {
clearInterval(this.interval);
this.interval = null;
}
updateDisplay() {
this.element.textContent = this.seconds;
}
}
// Usage
const countdownElement = document.getElementById('countdown');
const countdown = new Countdown(countdownElement, 10);
countdown.start();
Conclusion
When working with setTimeout
and setInterval
in object-oriented code, always be mindful of the context in which your callback functions will run. Use one of the techniques described above to ensure that your callbacks can access the proper object properties and methods.
Note: This article was migrated from the original A thousand nodes blog (2012)