-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
setInterval keeps drifting over time #21822
Comments
Afaik we had #7346 but didn't implement any drift-prevention mechanisms. This is still not fixed in a few browsers as there is no spec/agreement on the best solution. Currently, Chrome and Edge seem to correct the interval for each next execution (aligns with the results you are getting) but Firefox and reportedly Safari doesn't hence you should get the same results as with node. Related to whatwg/html#3151. Personally, I think it'd be better to implement something similar to Chromiums approach to fix this (fix the drift before/while |
Correct, i actually did a deep dive through most setInterval related bug tickets before opening this one, since most of them got eventually closed and haven't really came to any definite conclusion and had no activity on them for 6+ months. I was personally really surprised by the different behavior. In node uptime of weeks/months is completely normal for a node server, when it comes to websites users don't typically spend that amount of time on a single page so drift of couple of ms isn't a big deal there. Considering setInterval is just a "neat" way to setup re-curing timeout, doesn't such "drifty" behavior defeat the purpose of having it in the first place? Another great example that completely confuses everyone is setInterval in NW.js
When it comes to electron i have no idea, but as that is mixed context by default, so will most likely drift. The lack of spec is a big problem here, but if this isn't going to change, i think it would be worth adding a note about the drift in the timers section, as judging by the previous bug tickets i am not the only one that fell into this trap, some "official" stance on this would go a long way. |
Drift correction is a very problematic topic. E.g., chromium corrects for drift but only if it's less than some arbitrary threshold for each run. If it exceeds that then it just ignores the drift altogether and behaves worse than Node. There are also loop blocking concerns when it comes to drift because it's possible to end up in a world where the interval just stalls the loop forever. The logic has to be quite complex which in my opinion isn't worth the effort. If one were to look at Linux timers, those don't even guarantee to be executed on the correct order of milliseconds for long-running timers. |
Exactly. I ran the issue code on my chromium, it behaves so weird. In a new empty tab, it almost do the drift correction, but where the page has few animations/updates running it behaves more worse -- drift correction completely stops. |
Here is my understanding of the cause of the behavior described in this issue. I am stating it here for anyone who wants to debate or investigate a fix. No guarantee that I'm correct in my observations. High level
Timers are not guaranteed to go off precisely when they are scheduled, for example because the event loop is busy doing other things. Each time the timer is late there is drift D. The logic to re-arm the timer is unaware of the drift and schedules it to run again after time interval T -- so it will go off after T + D relative to when the timer previously went off. This drift is cumulative: the timer will not get closer to the expected expiration time than it was in the previous iteration (as long as the system clock is consistent), but it may continue to drift. I see two possible forms of drift, assuming the underlying system clock is accurate.
Implementation specificsSee
Comments
In principle, time checks (see Doing this would require introducing more If this approach were taken I would want to see overheads measured:
Thoughts on other designs? Comparison to libuvIt is worth noting that libuv's timer implementation has similar properties. Timers are re-armed before executing their callbacks (see |
I don't think we'll ever "fix" type 1. If the loop is busy, your timer will not be called until the queue is empty. I don't see this changing, maybe there is some way to make a user library with threads and V8 interrupts that would get around it. As for type 2... I think I remember this being discussed in the past. I don't remember the specific of what was discussed and changed or not, though.
This is actually a pretty significant regression, particularly if this also had to be applied to http timeouts. We aren't guaranteed how fast object-map (or |
(Also, if there is a way to design a user library that in some way can better adjust to re-scheduling timers more correctly but at an added cost, that may also be a more appropriate route to fix type 2.) |
Can I just point out that as an embedded programmer who has been looking to Node.JS as a direction to move in my work, a setInterval that drifts is... a cause for pause. I guess I was hoping it was more like an interrupt being fired in an embedded controller. That is quite possible not important to most of you, but I thought I might raise that point for us embedded guys. |
aside from timers, javascript doesn't have interrupts, so don't expect to see anything like interrupts in the javascript ecosystem. |
Yes, I understand it's not actually doing interrupts. And yet it is "event driven" which is very much interrupt adjacent. The distance between those two concepts is positive, but not large. In my (never) humble opinion. The point is that where I would have a timer interrupt in an embedded device which might do something every minute, I would expect to be able to setInterval in Node.JS and over the course of an hour, both should do the function 60 times. Exactly 60 times. |
There's been no further activity on this for over a year. With the understanding that there's likely some improvements that could potentially be made, those won't happen unless someone has a concrete proposal in the form of a pull request. Going to close the issue for now but can reopen if necessary |
For anyone stumbling across this issue, I played with this some today and ended up with this script: const start = performance.now();
let tick = 0;
(function step() {
setTimeout(step, 0);
const elapsed = () => performance.now() - start;
while (elapsed() / 1000 > tick) {
let driftT = elapsed() - (tick * 1000);
console.log(`tick: STEP: ${tick}, Drift: ${driftT}`);
tick++;
}
})(); |
Hi, first i should point out that this behavior doesn't match what is happening in the v8 inside of latest chrome (67.0.3396.99).
code demonstrating the issue:
The time compensation for setInterval changed drastically between 8.x and 10x (while 10x being more proper).
However setInterval still appears to be slipping over time.
output from the above code:
This shows a drift of 12ms over the 30 second testing period.
In production i am using setInterval with 60 000ms delay, but since the system tends to have very large uptime, slippage from the starting point is starting to become an issue.
The text was updated successfully, but these errors were encountered: