Recursive function call with setTimeout limited to ~200 calls per second, why?

Viewed 109

This code recursively calls the same function with a setTimeout of 1 millisecond, which in theory should call the function 1000 times per second. However, it's only called about 200 times per second:

This behavior happens on different machines and different browsers, I checked if it's has something to do with the maximum call stack, but this limit is actually way higher than 200 on any browser.

const info = document.querySelector("#info");
let start = performance.now();
let iterations = 0;

function run() {  
  if (performance.now() - start > 1000) {
    info.innerText = `${iterations} function calls per second`;
    start = performance.now();
    iterations = 0;
  }
  
  iterations++;
  setTimeout(run, 1);
}

run();
<div id="info"></div>

2 Answers

The delay argument passed to setTimeout and setInterval is not a guaranteed amount of time. It's the minimum amount of time you could expect to wait before the callback function is executed. It doesn't matter how much of a delay you've asked for, if the JavaScript call stack is busy, then anything in the event queue will have to wait.

Also, there is an absolute minimum amount of time you could reasonably expect a callback to be called after which is dependent on the internals of the client.

From the HTML5 Spec:

This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.

I once read somewhere that it was around 16ms so setting a delay of anything less than that shouldn't really change the timings at all.

There’s a limitation of how often nested timers can run. The HTML5 standard says: 11: "If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4."

This is true only for client-side engines (browsers). In Node.JS this limitation does not exist

HTML Standard Timers Section
Very similar example from javascript.info

Related