fbpx Skip to content

Aquent | DEV6

Understanding JavaScript Timers and Intervals

Written by: Quazi Sharar

JavaScript has become one of the most popular programming languages in the world. It’s the only programming language that browsers understand and, with the introduction of Node.js, it is rapidly gaining popularity in the backend as well.  JavaScript is amazing but it also come with a few quirks.  One of these quirks is how JavaScript handles its timers and intervals.

As we all know, JavaScript is single threaded.  However, since it is single threaded, it needs to deal with the asynchronous nature of things and utilize something called an event loop to have a non-blocking model.  If you don’t know about the event loop, I highly recommend reading about it from this article or watching this video. It is essential for any JavaScript developer to thoroughly understand the event loop.

Since JavaScript is single threaded, it means that everything is streamlined through a queue and only one thing can happen at a time.  So, what happens when you introduce timers and intervals to this queue?  What happens if the timer is ready to timeout but the queue is blocked?  What if the interval has fired once, twice, thrice….and the queue is still blocked?  Does the queue get cleared for the interval?  Does the queue give preference or priority?  Why isn’t it guaranteed that the interval will be at exactly the specified interval time?

Let’s take a deeper look into these questions and see what happens when the following piece of code is executed.

setTimeout( doSomethingOnce, 10);
setInterval( doSomethingRepeatedly, 10);

In an ideal synchronous world, the doSomethingOnce function above will run exactly after 10 milliseconds after the timer starts.  Following that, the doSomethingRepeatedly function will repeatedly run exactly every 10 milliseconds.  However, the world is not ideal and, more importantly, the world is asynchronous.  So, let’s introduce an asynchronous mouse click right after the timer initiates.  The diagram below shows how the flow works.  It looks like a lot to digest but let’s break it down.

asynchronous timer flow

Starting from 0ms the timer starts and, right after that, a mouse click event occurs.  After that, the interval initiates.  Let’s assume the mouse click has a handler attached to it and, at a certain time in the future, it returns with a callback.

Around the 10ms mark, the timer fires but isn’t immediately executed. After the initial block of JavaScript code completes, the browser looks to see what it can execute next (i.e. what is queued up).  In this case, at around the 20ms mark, the mouse click callback kicks in as well as the timer.  The browser then picks up the mouse click callback and executes it.

Upon completion, it will look for the next item in the queue which is the timer.  Here is where we notice the discrepancy in time.  One would assume that the timer will be executed 10ms since initiation but, in reality, it is getting executed at the 30ms mark.

Also, while the mouse click callback is executing, there is an interval event that is fired and put into the queue.  Before the timer and the first interval event can complete execution, another interval event is fired.  This one is dropped!  Why?  Because there is already another interval event lined up and running these intervals back to back will break the essence of “intervals” in JavaScript.  After the timer is cleared, the first queued interval is executed. Once again, notice the discrepancy of when the interval is expected to execute and when it is actually executed.

Finally, there is a period of time the queue is empty, and again there is another interval event at 50ms and it is executed immediately – this time without any delay. 

Revisiting the initial block of code, let’s summarize some key observations from the diagram:

setTimeout( doSomethingOnce, 10);
setInterval( doSomethingRepeatedly, 10);
  1. The earliest doSomethingOnce will run is within 10ms after starting the timeout counter.  It does not guarantee that it will run exactly at 10ms of time from initiation.
  2. The minimum time between two subsequent doSomethingRepeatedly executions can be 10ms, the maximum is unknown, and the minimum isn’t guaranteed.
  3. There cannot be back to back interval events in the queue.  The earliest one will be given priority and all the latter ones will be dropped until the queue is cleared.

That, essentially, is how the setTimeout and setInterval functions work!  Most likely you won’t measure or anticipate how these functions execute by the millisecond but, next time you use them, you’ll know exactly what to expect if you are looking at your clock and your functions are late to arrive!