It would seem that in general, browsers will in certain cases modify, even beyond a minimum clamp, the actual time interval that setInterval uses. For instance, I have the following code:
function start() {
window.setInterval(function() {
update();
}, 1);
}
lastTime = new Date;
numFrames = 0;
lastFrames = 0;
function update() {
numFrames++;
if (new Date - lastTime >= 1000) {
lastFrames = numFrames;
numFrames = 0;
lastTime = new Date;
}
}
Here, lastFrames will give us the number of frames over what is approximately the past second. When used in Chrome, Firefox, and Safari, this code doesn’t run at one millisecond. Of course, each browser has an arbitrary minimum time between setInterval calls, so this is to be expected. However, as the page continues to run, the frame rate will continue to decrease, even if the tab is still focused. The only way I’ve found to fix this is to make the browser do something. Something along these lines seems to make the browser run setInterval as fast as it can:
function start() {
window.setInterval(function() {
update();
}, 1);
}
lastTime = new Date;
numFrames = 0;
lastFrames = 0;
function update() {
numFrames++;
if (new Date - lastTime >= 1000) {
lastFrames = numFrames;
numFrames = 0;
lastTime = new Date;
}
//doIntensiveLoop, processing, etc.
}
Thus, my question is this: What is the browser looking for to justify running setInterval closer to what I ask it to?
EDIT: The HTML5 spec says that browsers should not allow setInterval to run at an interval lower than 4ms.
I think first, we have to ask ourselves what we expect from Interval functions:
They have to maintain the context: an interval in which you could not increment a counter reliably would be quite disastrous
They should execute whatever is in the function, which has priority over the execution interval (same here, that timer has to go up before we increment again)
It should have a separate execution stack from the rest of our js code (we don’t want to wait for those timers to finish their business until the rest starts executing);
They should be aware of the context they’re in, no matter how vast it is (we want to be able to use jQuery inside our timer functions, for example).
So, as Matt Greer said above, Intervals are by design not exact, and that’s mainly because we do not really expect them to be exact, but to be reliably executing code at a given time.
If you have a look at the Chromium implementation, you will see that the Implementation of setTimeout and setInterval is based on the DOMTimer::install, which gets passed the execution context, the action and if the timer is a single shot
This gets passed to the RunloopTimer, which executes the loop with the help of system timers (as you see here)
(by the way, chromium installs a minimum of 10ms for the interval, as you can see here)
Every Execution of the action is handled here, which does no assertion whatsoever over the time passed since the last execution being over or under a certain timing limit.
In contrary, it only asserts that the Timer nesting level does not get too deep by slowing down timers that are using too much resources / running too slowly for the given interval with this:
augmentRepeatInterval simply adds more milliseconds to the interval:
So, what can we conclude?
Measuring the time accuracy of Intervals or Timeouts is a waste of time. The thing you should and can care about is that you don’t set the intervals too low for the things you want to execute in the function. The browser will do the best it can to execute your intervals and timeouts in a timely fashion, but it won’t guarantee exact timing.
The Interval execution depends on the environment, the browser, the implementation, the version, the context, the action itself and so on and so on. It is not meant to be exact, and if you want to program something exact with setTimeout or setInterval, you’re either crazy now or will go crazy later.
You said that in your code, when you added heavy execution to the functions, the timer got more accurate. This can be for different reasons (maybe it gets more memory, more exclusive cpu time, more workers and so on). I’m very interested in that, but you did not provide the code that did the heavy execution yet. So if you want some answers on that, please provide the code, because it’s difficult to assume anything without it.
But whatever it is that makes your intervals run more timely, it’s not reliable in any way. You will most likely get all kinds of various results if you start measuring on different systems.
UPDATE
Other than that, code that does not lead to anything may be as well optimized (not executed, executed only once, executed in a better way) by the browser js engine. Let me give an example based on yours (all things executed in chromium):
You will find that the first execution after you hit
startwill take ages, whereas the further executions don’t (at least in webkit). That’s because the code in the iteration does not change anything, and the browser recognizes that after the first execution and simply does not execute it anymore.Let’s look how it changes if the execution has to maintain a binding to an outer variable (
kin this case):Ok, here we have some impact on the execution time, but it is still quite fast. The browser knows that the for loop will always do the same thing, but it executes it once. So what if the iteration really does create a huge string, for example?
This puts the browser in a world of hurt, because it has to return this millions-of-characters string. Can we make that more painful?
This array concatenation cannot be optimized and will choke almost any browser slowly and painfully.
So, in your case, the heavy execution may have lead for more memory to be reserved and instantly freed by the optimizer. May be that breath of fresh air and extra memory and idle cpu made your function jump with joy, but as I said, there’s nothing reliable there without having looked at your heavy execution code.