By default, setTimeout
uses the system's internal timer, which has a granularity of 16.66...ms (a frequency of 60 Hz, aka 60 frames per second). Some browsers override this (which is actually really bad for the processor - although in the case of Internet Explorer it only does this if the computer is specifically set to "High Performance" mode, otherwise it uses the less harmful 60fps limit)
However, even if the setTimeout
were perfectly accurate, it takes time to process the JavaScript within. As much as we'd like it to be, code is not truly realtime. This means that, even with a compeltely accurate 1,000ms timer, after a few thousand iterations you will start to notice a visible discrepency.
All in all, setTimeout
(and by extension setInterval
, which is basically just setTimeout
with an automatic renewal) is unreliable. If you want reliability, you must use delta timing.
Delta timing is when you save the time at which you start the timer, and compare it to the time when the timer fires. A simple example is this:
var start = new Date().getTime();
setInterval(function() {
var now = new Date().getTime(), delta = now-start;
console.log("Timer fired after "+delta+"ms");
// chances are, you'll see numbers between 984 and 1016, unless your browser
// is a processor-killing, rainforest-destroying monster :p
// even then, you'll probably see some 999s and 1001s, depending on how
// busy your computer is with other tasks. Open a YouTube video, you'll see.
},1000);
This becomes even easier with the new requestAnimationFrame
function. However, keep in mind that it's a new feature, and most browsers haven't adopted it without a vendor prefix. Internet Explorer is the only browser that I'm aware of that has. And people say that IE sucks!
Anyway, assuming you have a suitable polyfill, it would look like this:
var previous = new Date().getTime(); // or Date.now() if you're feeling brave...
// or are already excluding old browsers via canvas use
requestAnimationFrame(function(now) {
var loopit = arguments.callee, delta = now-previous;
previous = now;
console.log(delta); // should be around 16 or 17
requestAnimationFrame(loopit);
});
Seems like a lot of work for something that's basically just a glorified setTimeout
, right?
Well, yes. But one thing I particularly like is that requestAnimationFrame
will wait until a frame is actually being rendered. In particular, this means that if you are looking at a different tab, requestAnimationFrame
will wait for you to come back. I use this to delay auto-updating AJAX requests, allowing the user to actually see the update then and there, rather than coming back and saying to themselves "now... has it updated? I don't know, I don't remember what it was before..." And instead they get to admire some neat transitions and come to you telling you how cool it is that it waits for you to come back before updating ;) When your users think something is cool, then that's really cool. (Protip: carousels are not cool - I know many people who would do well to learn this!)
Anyway, I've sort of rambled a bit. I don't know if I've answered your question, but I certainly hope that I've enlightened you some, and that you've found this interesting, if tangential.
Have fun!