10

I am trying to run a function for each milliseconds, In order to achieve so, I just preferred setInterval concept in javascript. My code is given below,

HTML:

<div id=test>0.0</div>

Script:

var xVal = 0;
var xElement = null;

xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 1);


function startWatch(){

    xVal += 1;
    xElement.innerHTML = xVal;

}

so the above code is working fine. But while I am testing the result with a real clock, the real clock requires 1000 milliseconds to complete 1 second, at the same time the result require more than 1000 milliseconds to complete a second.

DEMO

Can anybody tell me,

Is there any mistakes with my code? If yes then tell me, How to display milliseconds accurately.?

Rajaprabhu Aravindasamy
  • 66,513
  • 17
  • 101
  • 130
  • 4
    The browser is not a realtime system and any segment of JS can take > 1 millisecond to complete. Using 1 millisecond intervals is not a good solution. – Matt S Sep 18 '13 at 19:26

6 Answers6

9

There are no mistakes in your code, but JavaScript timers (setInterval and setTimeout) are not precise. Browsers cannot comply with such a short interval. So I'm afraid there is no way to precisely increment the milliseconds by one, and display the updates, on a web browser. In any case, that's not even visible to the human eye!

A precise workaround would involve a larger interval, and timestamps to calculate the elapsed time in milliseconds:

var start = new Date().getTime();
setInterval(function() {
    var now = new Date().getTime();
    xElement.innerHTML = (now - start) + 'ms elapsed';
}, 40);
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
6

You can't. There is a minimum delay that browsers use. You cannot run a function every millisecond.

From Mozilla's docs:

...4ms is specified by the HTML5 spec and is consistent across browsers...

Source: https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout#Minimum.2F_maximum_delay_and_timeout_nesting

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
4

The DOM can't actually update 1000 times per second. Your monitor can't even display 1000 frames in one second, for that matter. Calculate the difference between the start time and current time in milliseconds within your function and use that:

(function(){
    var xElement = document.getElementById("test");
    var start = new Date;

    (function update(){
      xElement.innerHTML = (new Date - start);

      setTimeout(update, 0);
    })();
}();

Updated fiddle

Paul
  • 139,544
  • 27
  • 275
  • 264
3

You can't do so using your method because of the delay rendering the HTML and running the interval. Doing it this way will display the time correctly at about 60FPS.

http://jsfiddle.net/3hEs4/3/

var xElement = null;
var startTime = new Date();

xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 17);


function startWatch(){
     var currentTime = new Date();
     xElement.innerHTML = currentTime - startTime;   
}

You might also want to look into using requestanimationframe instead of a hardcoded setInterval like that.

PherricOxide
  • 15,493
  • 3
  • 28
  • 41
0

The setInterval callback probably does not happen with millisecond accuracy, since the thread the timer is running on might not even actually be running when the time is up, or the browser throttles events, or any other of quite a few things.

In addition, since most Javascript engines are single threaded, what the implementation of setInterval might do is once it triggers, run your callback, and then reset the clock for the next call. Since you're doing some DOM manipulation, that might take several milliseconds on its own.

In short, you're expecting a Real Time Operating System behavior from an interpreter running inside of another application, on top of what is more than likely not an RTOS.

Matt Sieker
  • 9,349
  • 2
  • 25
  • 43
0

I had the same question and couldn't find any working solution, so I created one myself. The code below essentially calls five setTimouts every 5 ms, for each ms between 5 and 10. This circumvents the minimum 4 ms constraint, and (having checked in Firefox, Chrome, and Opera) works fairly well.

const start = performance.now();
let newNow = 0;
let oldNow = 0;

const runner = function(reset) {
    // whatever is here will run ca. every ms
    newNow = performance.now();
    console.log("new:", newNow);
    console.log("            diff:", newNow - oldNow);
    oldNow = newNow
    if (newNow - start < 1000 && reset) {
        setTimeout(function() {
            runner(true);
        }, 5);
        for (let i = 6; i < 11; i++) {
            setTimeout(function() {
                runner(false);
            }, i);
        }
    }
};
runner(true);

It could of course be written more elegantly, e.g. so that you can more easily customize things like the graduation (e.g. 0.5 ms or 2 ms instead of 1 ms), but anyway the principle is there.

I know that in theory you could call 5 setIntervals instead, but that would in reality cause a drift that would quickly ruin the ms precision.

Note also that there are legitimate cases for the use. (I for one need continual measurement of touch force, which is not possible otherwise.)

gaspar
  • 898
  • 1
  • 13
  • 26