0

I am working on a project that relies heavily on a timer. I have seen 1-2 scenarios where the timer slows down and is not ticking every second as it should, but 1 second/tick takes 2-3 seconds. Usually it is due to the entire computer slowing down and becoming very laggy. I cannot replicate it, but some users have reported similar findings, and once they reset their computer, everything is as it should be.

My question is, how does the timeout/interval work? If the browser is bogged down, does it slow down the timer, or is it based off the CPU/system time? Also, has anyone else had something similar happen, and if so, what types of things can I do to rectify the situation (make a 15 second timer actually fire an event after 15 seconds even if the system is slow)?

j08691
  • 204,283
  • 31
  • 260
  • 272
Tim Withers
  • 12,072
  • 5
  • 43
  • 67
  • See my answer on [Are there any standards for mobile device web browsers in terms of thread sleeping?](http://stackoverflow.com/questions/10739835/are-there-any-standards-for-mobile-device-web-browsers-in-terms-of-thread-sleepi) – Bergi Oct 23 '13 at 17:43
  • Also related: [Chrome: timeouts/interval suspended in background tabs?](http://stackoverflow.com/questions/6032429/chrome-timeouts-interval-suspended-in-background-tabs). – Brad Christie Oct 23 '13 at 17:43
  • 2
    As an aside: may I ask what you're doing every 15 seconds that it needs to be on (or near) the tick? – Brad Christie Oct 23 '13 at 17:46
  • They are typing a set amount of text, and I need to calculate words per minutes. There are different intervals, 15 seconds, 30 seconds, 1 minute, etc. – Tim Withers Oct 23 '13 at 17:47
  • 2
    Measurement of WPM is done as an average over several minutes because of variations of text and human comprehension. It is not reasonable to expect such a small time frame to be accurate. – StingyJack Oct 23 '13 at 17:51
  • @StingyJack That is 100% true, but I also don't expect users to type "aaa aaa aaa sss sss" for 45 seconds, only 15. If we were talking milliseconds, I wouldn't care a bit. but when we are lengthening the timer by 2-3 times, then it is bad. – Tim Withers Oct 23 '13 at 17:57
  • But you aren't lengthening it by 2-3 times. Per your statement, you are going from 15 seconds to 16, not from 15 seconds to 45. This is not a real time device, so some imprecision in timing should be expected. – StingyJack Oct 23 '13 at 18:03
  • `but 1 second/tick takes 2-3 seconds` @StingyJack If it was 16 seconds instead of 15, I wouldn't care. – Tim Withers Oct 23 '13 at 18:07
  • If you ever want accurate time, *get the time* using `new Date().getTime()`. – Ry- Oct 23 '13 at 18:11
  • Do you mean that EACH 1 second interval is taking 2-3 seconds instead? – StingyJack Oct 23 '13 at 18:11
  • @StingyJack, yes. A 15 second timing, in very very very rare cases (1 in 10k maybe) becomes 30-45 seconds. Once the computer is restarted, it is fixed. Otherwise it is more or less 15 seconds. That is what I have been saying. – Tim Withers Oct 23 '13 at 18:20

2 Answers2

4

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!

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 3
    Actually, both Firefox and Chrome have unprefixed `requestAnimationFrame`. Firefox since version 23, Chrome since version 24, Safari since version 7, Opera since version 15. – Ry- Oct 23 '13 at 18:10
1

Here is a usefull link on setTimeout. (Its by John Resig)

sum-up of article:

  • JavaScript engines only have a single thread, forcing asynchronous events to queue waiting for execution.
  • setTimeout and setInterval are fundamentally different in how they execute asynchronous code.
  • If a timer is blocked from immediately executing it will be delayed until the next possible point of execution (which will be longer than the desired delay).
  • Intervals may execute back-to-back with no delay if they take long enough to execute (longer than the specified delay).
R. Oosterholt
  • 7,720
  • 2
  • 53
  • 77
  • 4
    Please don't just link to an off-site resource. For all you know, that server might spontaneously explode tomorrow, and your answer will become completely useless. Instead, please quote relevant sentences or paragraphs from the post, and provide the link for citation and further reading. – Niet the Dark Absol Oct 23 '13 at 17:47
  • good point, but the article is pretty detailed/in depth. The "relevant sentences" might be quite a lot... – R. Oosterholt Oct 23 '13 at 17:49