Something that has always bugged me is how unpredictable the setTimeout()
method in Javascript is.
In my experience, the timer is horribly inaccurate in
If you're using setTimeout()
to yield quickly to the browser so it's UI thread can catch up with any tasks it needs to do (such as updating a tab, or to not show the Long Running Script dialog), there is a new API called Efficient Script Yielding, aka, setImmediate()
that may work a bit better for you.
setImmediate()
operates very similarly to setTimeout()
, yet it may run immediately if the browser has nothing else to do. In many situations where you are using setTimeout(..., 16)
or setTimeout(..., 4)
or setTimeout(..., 0)
(i.e. you want the browser to run any outstanding UI thread tasks and not show a Long Running Script dialog), you can simply replace your setTimeout()
with setImmediate()
, dropping the second (millisecond) argument.
The difference with setImmediate()
is that it is basically a yield; if the browser has sometime to do on the UI thread (e.g., update a tab), it will do so before returning to your callback. However, if the browser is already all caught up with its work, the callback specified in setImmediate()
will essentially run without delay.
Unfortunately it is only currently supported in IE9+, as there is some push back from the other browser vendors.
There is a good polyfill available though, if you want to use it and hope the other browsers implement it at some point.
If you are using setTimeout()
for animation, requestAnimationFrame is your best bet as your code will run in-sync with the monitor's refresh rate.
If you are using setTimeout()
on a slower cadence, e.g. once every 300 milliseconds, you could use a solution similar to what user1213320 suggests, where you monitor how long it was from the last timestamp your timer ran and compensate for any delay. One improvement is that you could use the new High Resolution Time interface (aka window.performance.now()
) instead of Date.now()
to get greater-than-millisecond resolution for the current time.
.
REF; http://www.sitepoint.com/creating-accurate-timers-in-javascript/
This site bailed me out on a major scale.
You can use the system clock to compensate for timer inaccuracy. If you run a timing function as a series of setTimeout calls — each instance calling the next — then all you have to do to keep it accurate is work out exactly how inaccurate it is, and subtract that difference from the next iteration:
var start = new Date().getTime(),
time = 0,
elapsed = '0.0';
function instance()
{
time += 100;
elapsed = Math.floor(time / 100) / 10;
if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }
document.title = elapsed;
var diff = (new Date().getTime() - start) - time;
window.setTimeout(instance, (100 - diff));
}
window.setTimeout(instance, 100);
This method will minimize drift and reduce the inaccuracies by more than 90%.
It fixed my issues, hope it helps
This is a timer I made for a music project of mine which does this thing. Timer that is accurate on all devices.
var Timer = function(){
var framebuffer = 0,
var msSinceInitialized = 0,
var timer = this;
var timeAtLastInterval = new Date().getTime();
setInterval(function(){
var frametime = new Date().getTime();
var timeElapsed = frametime - timeAtLastInterval;
msSinceInitialized += timeElapsed;
timeAtLastInterval = frametime;
},1);
this.setInterval = function(callback,timeout,arguments) {
var timeStarted = msSinceInitialized;
var interval = setInterval(function(){
var totaltimepassed = msSinceInitialized - timeStarted;
if (totaltimepassed >= timeout) {
callback(arguments);
timeStarted = msSinceInitialized;
}
},1);
return interval;
}
}
var timer = new Timer();
timer.setInterval(function(){console.log("This timer will not drift."),1000}
Are there any tricks that can be done to ensure that
setTimeout()
performs accurately (without resorting to an external API) or is this a lost cause?
No and no. You're not going to get anything close to a perfectly accurate timer with setTimeout()
- browsers aren't set up for that. However, you don't need to rely on it for timing things either. Most animation libraries figured this out years ago: you set up a callback with setTimeout()
, but determine what needs to be done based on the value of (new Date()).milliseconds
(or equivalent). This allows you to take advantage of more reliable timer support in newer browsers, while still behaving appropriately on older browsers.
It also allows you to avoid using too many timers! This is important: each timer is a callback. Each callback executes JS code. While JS code is executing, browser events - including other callbacks - are delayed or dropped. When the callback finishes, additional callbacks must compete with other browser events for a chance to execute. Therefore, one timer that handles all pending tasks for that interval will perform better than two timers with coinciding intervals, and (for short timeouts) better than two timers with overlapping timeouts!
Summary: stop using setTimeout()
to implement "one timer / one task" designs, and use the real-time clock to smooth out UI actions.