Fix JavaScript setTimeout not working

The `setTimeout` function can come in handy for delaying execution. We go over few reasons why the settimeout function is not working - ranging from using parentheses to incorrect scope or this keyword.

Apr 15, 2022 | Read time 7 minutes

🔔 Table of contents

When creating front end interactions with JavaScript, the setTimeout function can come in handy for delaying execution. For example we might want to wait for an animation to finish then execute a loading feature.

Another example is when we want to do count down timers - execute a modal popup when a timer count down expires (eg after 60 seconds or so).

What is the setTimeout function?

The setTimeout function is a global JavaScript function that executes a specific function when the timer expires.

    setTimeout(function, delay, arg1, arg2...)

From the above method signature, we can see that the first parameter we expect is a a function definition, the second parameter is the delay duration (in milliseconds).

So if we specify setTimeout(doSomething, 1000), the doSomething function will execute after 1000 milliseconds (1 second).

The arg1 and arg2 are parameters that can be passed to the doSomething function. Along with the delay, the arg parameters are optional.

Issue 1 - Not passing in functions correctly

A common issue when using the setTimeout function is passing in the function to execute correctly. Often, the function is passed as a function call instead of the function signature. Solution: remove the parentheses ()

As an example of this common problem, lets say we have a function that creates a alert when the timer ends in 2 seconds.

function completeAlert(){
    alert('Timer completed');
    return true;
}

setTimeout(completeAlert(), 2000); /* <-- Notice the use of parentheses here */

The problem with the above is that setTimeout expects a function definition, but we are passing in the execution result of completeAlert() (using the parentheses) - in this case it is returning true.

In JavaScript, functions are first-class objects, and as such they can be passed in as a parameter. So to fix our problem above, we just need to remove the parentheses and pass in the function name.

function completeAlert(){
    alert('Timer completed');
    return true;
}

setTimeout(completeAlert, 2000); /* <-- No parentheses - passing only the function definition */

💡 Tip: Using anonymous and arrow functions

If we are using modern Javascript, we can use anonymous or arrow functions to make our calls simpler for short functions. This will reduce the amount of code we have to write - eg creating a separate function definition and coming up with specific names, etc.

setTimeout( () => alert('Timer completed'), 2000 ) /* Using arrow functions  */

setTimeout(function() { alert('Timer completed') },2000); /* Using anonymous functions  */

Issue 2 - Incorrect use of parameters

Another common problem that we can see with setTimeout function usage is passing parameters. For example, from the definition, the third and onward parameters are values that we can pass to the function to execute:

function testFunction(x, y) {
    console.log(x);
    console.log(y);
}

setTimeout(testFunction, 2000, "Hello", "Bob")‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍;

This will result in the following being printed to console after 2 seconds:

"Hello"
"Bob"

Now since the parameters are not named adequately, we can easily pass in the wrong values.

Issue 3 - this keyword and scope incorrect

When passing in a function to the setTimeout and we use the this keyword in our function, we might get different results than the expectation.

This is due to when a function is executed as a parameter to setTimeout, the execution context is different to the execution context of the function!

Sorry that was a mouthful, but heres an example to further demonstrate:

const fruits = ['apple', 'banana', 'peach'];
fruits.print = function (index) {
  console.log(this[index]); // "this" refers to the fruits array 
};

fruits.print(1); // prints "banana"

Now if we use the above function in a setTimeout call, we get the following:

setTimeout(fruits.print, 1000, 1); // prints "undefined" after 1 seconds

Now this will print out undefined because the this keyword is executed in the context of the setTimeout function and is therefore not defined. It will then take the window global object ([object Window]).

Since the window object will have have a 1 index, undefined will be printed!

💡 Solution to fix this: use anonymous or arrow functions!

The usual way to fix this type of issue is to use anonymous or arrow functions to wrap it all up:

setTimeout(function(){fruits.print('1')}, 2.5*1000); // prints "banana" after 2.5 seconds

setTimeout(() => {fruits.print('1')}, 2.5*1000); // prints "banana" after 2.5 seconds

Issue 4 - Delay duration is not accurate

If you are requiring the setTimeout functions to execute on the dot, there can be some scenarios when this is not the case.

This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected. https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers

Late timeouts or timeouts that execute inaccurately could be due the following issues:

  • browser or OS is busy with other tasks
  • nested timeouts - timers can be nested - after 5 nested timers, the minimum timer delay is 4 milliseconds.
  • inactive tabs - this can be dependant on the browser - Chrome will have a minimum of 1 second for inactive tabs. Firefox for Android may unload timeouts after 15 minutes of inactivity.
  • timeouts after pageload - timeouts are calculated on the page load and not during loading. For example in firefox, will fire the timeout once the load event is fired.

Conclusion

The setTimeout function is a great way to execute code after a specified delay duration. Can be used in use cases such as timers. In this post we went over the various issues when using the setTimeout function.

Bugs can arise when we pass in a function call instead of a function definition (eg using providing the function name with parentheses), not using anonmyous or arrow functions to avoid this or scoping issues.

Additionally we need to be weary that the API does not guarantee that the code will run exactly to schedule - there could be multiple factors that affect this such as browser/OS load, tab inactivity and nested timeout calls.

👋 About the Author

G'day! I am Huy a software engineer based in Australia. I have been creating design-centered software for the last 10 years both professionally and as a passion.

My aim to share what I have learnt with you! (and to help me remember 😅)

Follow along on Twitter , GitHub and YouTube