When working with timers, setting a delay of 0, does not equate to 0ms or “instant” execution. This longer than expected delay may occur because the OS/browser/system is busy with other tasks or because the timer has been throttled.
Note: for the purpose of this discussion “instantaneous” or “instant” means code that can run immediately (e.g. code that was not placed on a queue by setTimeout or setInterval)
setInterval. This throttle means that
setInterval have a minimum delay that is greater than 0ms.
The minimum delay is:
- 4ms in browsers (per MDN)
- This throttle occurs when successive calls are triggered due to callback nesting or after a certain number of successive intervals
- 1ms in Node.js (per Node docs)
The implication being that setting a delay of 0ms will not happen instantaneously.
The timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks. One important case to note is that the function or code snippet cannot be executed until the thread that called
Not quite what we expected, is it? This is because even though we set a delay of 0, the code within a timer is placed on a queue and scheduled to run at the next opportunity, not immediately. Code that is currently executing must complete before functions on the queue are run.
Background aka “how this arose during development”
At my company, one of our current strategic initiatives is to enhance the scalability of our infrastructure. To that end, members of the DevOps team are conducting performance tests on our servers to better understand the maximum user load we can support while maintaining our KPIs. To facilitate this testing, the team is building a tool that simulates a play-through on our web application and randomizes when certain actions (e.g. button clicks, data submissions) occur. Once completed, this tool will be “spawned” across multiple server instances to hammer our infrastructure :).
A requirement of this tool is to both run “instantly” (to maximize hits per minute) and “randomly” (within specified ranges of time) to simulate user interaction.
While testing, we observed that when setting a timeout of 0, the overall play-through was taking longer than expected. After further research, we discovered that yes, a delay of 0, does not equal 0ms and is definitely not “instant”.
Now that we have a little background, let’s dive into tests we conducted to illustrate the minimum delay used by
Testing details and steps:
- Computer: Dell Latitude E5570. 16MB RAM, Processor: i7 2.60GHz. Windows 10
- Chrome - Version 66.0.3359.139
- Firefox - Version 60.0
- Node.js - Version 10.1.0
How the tests were performed:
In the browser:
run the following steps 5 times for each file
- load loop.html, loop-settimeout.html, or loop-setinterval.html in the browser
- open up developer tools
- view results
- clear cache
- refresh the browser
run each statement below 5 times
The code can be found here.
Note: Chrome provides more significant digits than Firefox and thus the times are rounded to the nearest whole number to provide equivalent levels of precision.
By comparing loops that do and do not utilize timers, our test results confirm that
setInterval indeed have a delay that is longer than expected when the delay is set to 0. The
setInterval results from the browser tests roughly equal 4ms (i.e. the throttle) multiplied by 10,000 (i.e. the length of the loop). In both Node.js and browser tests,
setTimeout performs significantly better than
All these glorious numbers are essentially telling us: if you want a piece of code to execute immediately, do not use timers.
For further reading, check out the additional resources below and happy coding!