1

I am developing a stopwatch application using Javascript/jQuery. The problem is that the milliseconds value is out of sync with REAL milliseconds. I am using function setInterval() with the interval of 1 millisecond, still it is causing this problem.

jsFiddle: http://jsfiddle.net/FLv3s/

Please help!

Rahul Desai
  • 15,242
  • 19
  • 83
  • 138
  • 1
    setInternal will only guarantee the minimum time to wait, it could be more – Scary Wombat Nov 14 '13 at 08:44
  • 1
    Do you really believe that code is executed in under 1ms every millisecond? I suggest you set the interval to e.g. 20ms and get the time including milliseconds from a Date object. – PurkkaKoodari Nov 14 '13 at 08:44
  • 1
    How do you know that each millisecond++ that you make, takes only 1 millisecond? That's wrong logic. Use system's own time – Hanky Panky Nov 14 '13 at 08:45

4 Answers4

5

Use setInterval to trigger updates, but use the system time (via new Date()) for the actual time calculations.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • 3
    which will also not be 100% accurate :) i.e: you might stop the whatch while setInterval was processing the new Date() on it's own rate, and on the screen you'll get a different result than in the *real world*. – Roko C. Buljan Nov 14 '13 at 08:47
  • @RokoC.Buljan: Whenever you start or stop the watch, you update your time calculation. You don't just leave it displaying whatever it had calculated at the most recent timer tick. – RichieHindle Nov 14 '13 at 09:26
2

To be honest, I tried nearly the same thing as you do now (Creating an accurate Metronome in Javascript only) - to make a long story short: To be absolutely accurate in terms of milliseconds (or lower) is sadly not (yet) possible with javascript only.

For more insight i recommend this question: Does JavaScript provide a high resolution timer?

or to be more precise this blog article: http://ejohn.org/blog/how-javascript-timers-work/

Best regards, Dominik

Community
  • 1
  • 1
Dominik
  • 2,801
  • 2
  • 33
  • 45
1

Program execution in any language, not just JavaScript, is not realtime. It will take a tiny amount of time to actually run the code to increment your counter and update the view, and that throws the "timing" off.

Additionally, many browsers have a "minimum timeout" length, which varies between 4 and about 16 (the latter being the computer's own clock timer), which will really mess with your code.

Instead, you should use delta timing.

var startTime = new Date().getTime();
setInterval(function() {
    var elapsed = new Date().getTime()-startTime;
    // update view according to elapsed time
},25);

If you're worried about it looking choppy, consider using requestAnimationFrame instead, to update the timer exactly once per frame - this has the added benefit of not updating when the user switches tabs (but it will still be timing them) because if the tab is not active then there's no need to redraw stuff.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • Why did you set the interval as 25? (Sorry for the terribly late reply!) – Rahul Desai Jan 09 '14 at 11:57
  • 1
    A 25ms interval corresponds to 40fps, which is more than fast enough for most applications. You can set it to any number you want really. Lower numbers are more precise but consume more CPU resources, whereas higher numbers are more CPU-friendly but less immediate. – Niet the Dark Absol Jan 09 '14 at 13:43
0

You can use the new performance object to get a more accurate time. Combine this with requestAnimationFrame instead of setInterval:

var startTime = performance.now(),
    currentTime,
    isRunning = true;

loop();

function loop(timeElapsed) {

    currentTime = performance.now();
    if (isRunning) requestAnimationFrame(loop);
}

Just subtract startTime from currentTime.

I left timeElapsed which contains time elapsed handed by rAF which you can may use also for something (or just ignore it).

One note though: not all browsers support this yet (and some may use prefix) so for mobile you need to use the standard system time object.

  • I googled but didnt find any documentation on the `performance` object. – Rahul Desai Jan 10 '14 at 03:43
  • @IamDesai just open console in f.ex.Chrome and start typing performance.. You can for example use `performance.now()` –  Jan 10 '14 at 05:13