Appropriately using setTimeout

If you just read that title and you’re saying “Man, fuck this guy. I know how to use freakin setTimeout”, this article isn’t for you (or you’re a 💩-bag who hates knowing things). The idea to write this spawned from seeing this method abused by junior/new developers and I realized there isn’t much guidance as to how, when and why to use it.

What happens when you call setTimeout?

Understanding how setTimeout works requires you to have some understanding of what the call stack, queue and the event loop are. If you’re not familiar with it here is a great talk you should watch that also explains a few things about how setTimeout works.

When you call setTimeout, it pushes your function to a WebAPI which then waits for the specified delay to push your function into the queue. As soon as there is room in the call stack, the event loop pushes your function on the stack to be called.

You may have seen people call setTimeout like so:

setTimeout(()=> {/* some code to execute */}, 0);

(You can actually omit the delay argument to get the same call) This pushes the function to our setTimeout WebAPI which adds it to the queue. The function will run as soon as the queue is cleared and there is room on the call stack. This is commonly used as a quick fix for long-running functions that are blocking the call stack.

What are some reasonable uses of setTimeout?

  • To set a minimum delay before execution of a function
  • Remove a blocking function from the call stack (if you can’t use a web worker)
  • As a quick mock for an async operation

Set a minimum delay before execution of a function

setTimeout(delayedFunction, 2000);

Well, duh. You want a function to be called after some period of time. Note that the delay you give is only the minimum time until the function is called. If there are other things blocking the stack it will delay it further. A few other reasons for variance in execution time can be found here.

This is commonly used for animation. This is ok, but with the Web Animations API becoming more mature, there will be less use cases for setTimeout. Additionally many animations can be handled by GPU accelerated CSS animations.

Remove blocking execution from the call stack

setTimeout(someHeavyFunction);

setTimeout can be a quick and easy fix to prevent long-running functions from blocking the call stack. However this shouldn’t be a permanent solution; favor using something like a web worker instead. They have pretty wide support if you don’t have to support old versions of Internet Explorer.

Before using setTimeout, make sure that the function is optimized. Obviously some operations can be complex and heavy no matter what but breaking up a large function into smaller segments and reducing the load on the browser may eliminate the need to use setTimeout. It also has the added benefit of making your code more concise and organized.

Most of the times I have seen setTimeout used this way, it was because there was a long, unnecessarily complex, poorly written function that someone didn’t want to pick apart and rewrite. If you use this and you’re not parsing some massive chunk of data, you probably did something shitty and need to take a step back and rethink.

“You better check yo self before you wreck yo self”
— Ice Cube

Mocking up async

const randomDelay = (maxDelay = 2000) => 
Math.floor(Math.random() * maxDelay);
setTimeout(sendResponse, randomDelay());

This is my most common use case for setTimeout. If I know that a component or view is going to be dependent on some async function or data fetch that is not yet available I will often use something like above to mock up the response with a random delay so I can ensure my view works if the data isn’t immediately available.

What is a bad use of setTimeout?

A big no-no is using setTimeout to fix a race condition (or a “data isn’t ready” bug for those pedantic readers who don’t like the use of the term race condition in a single threaded language like JavaScript). I see this far too often among new devs (and sometimes some not so new devs, yikes).

One of the hardest things I had to wrap my head around, and still sometimes get hung up on, is controlling the execution flow in an asynchronous environment. The community as a whole is still trying to iron out a good solution (Callbacks, Deferred/Promises, Async, Generators, etc.) At first glance setTimeout seems like a good solution for functions running out of order: it makes sense on the surface. However, it really is just a bandaid over what may be a larger issue in your code.

If you find yourself in a situation where you think that something is breaking because functions are not happening in the order you expected them too its fine to use setTimeout to test your theory or to push a quick fix but you should refactor it later.

Another solution such as using a promise or async function might be best. Wait for the promise/async to resolve and dispatch an event to let your function(s) know the data is ready. This is going to be:

  1. A far more solid implementation because the function is running based on the resolution of the promise/async and not some arbitrary timer that doesn’t know if the data is actually available or if there was an error
  2. Much less painful later when other things also depend on that data being available. Imagine you have dozens of functions that rely on this data (i.e. component/view render methods) that could be called at anytime, setTimeout would be all over the place.
You believing that using setTimeout to fix that race condition was a good idea

TLDR;

setTimeout is a nifty method in JavaScript and definitely has a place in the language but with all things, it can be used for good and evil.

  • Know how it works
  • Use it when you need it and it makes sense
  • Don’t use it to hide bigger underlying problems