简体   繁体   中英

Does JavaScript provide a high resolution timer?

Does JavaScript provide a high resolution timer?

I've written a few game engines from scratch, some in C, some in Java, and some in Flash. I always follow the same basic model when it comes to animations and interactive graphics. Create a basic class/structure with the following design:

void init() { /* Called once, preload essential resources here. */ }
void update(double time) { /* Updates game/animation state using high resolution time. */ }
void render(double time) { /* Updates screen graphics using high resolution time. */ }

void run()
{
    double time;
    init();
    while (!done)
    {
        time = queryTime();
        update(time);
        render(time);
    }
}

Time is so important to smooth animations and game state calculations. In native code Windows, I use QueryPerformanceCounter() and QueryPerformanceFrequency() to perform the role of queryTime() in each game loop and pass the time to update/render. In Java, I use System.nanoTime() .

What's the equivalent in JavaScript? That is, some function like queryTime() which returns a time value with a high degree of accuracy (sub millisecond). From what I've heard the best accuracy you can get in JavaScript is about 15 ms ... which is horrible for animation.

Almost all modern browsers provide a high resolution timer. It's the "High Resolution Time" W3C standard: http://www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp .

It allows you to get a sub-millisecond accurate timestamp by calling window.performance.now() . This returns a time stamp in ms, but it is a float so you still get sub-millisecond resolution.

Very old browsers may implement a "prefixed" version of this standard, eg WebKit based browsers used to implement window.performance.webkitNow()

Here is some code you can use to get the the accurate timestamp when available and fallback to standard precision otherwise:

if (window.performance.now) {
    console.log("Using high performance timer");
    getTimestamp = function() { return window.performance.now(); };
} else {
    if (window.performance.webkitNow) {
        console.log("Using webkit high performance timer");
        getTimestamp = function() { return window.performance.webkitNow(); };
    } else {
        console.log("Using low performance timer");
        getTimestamp = function() { return new Date().getTime(); };
    }
}

getTimestamp();

Note that this getTimestamp() function does not return a value that represents the current date/time. The returned value can only be used to measure time-periods, by subtracting two different timestamps. Eg

var t1 = getTimestamp();
//... some other code
var t2 = getTimestamp();
console.log("Time delta: " + (t2 - t1));

Instead of while (true) / setInterval , use recursive requestAnimationFrame . It will run smoother than timeout based animations. It provides timestamps if you need your animation to run at slower path.

See @h3r3's answer —it is the correct answer.

Milliseconds are the best you can hope for in JavaScript. And, like you said, it isn't very accurate. See Stack Overflow question Microsecond timing in JavaScript .

timer.js purports to provide up to microsecond resolution, but it is only available for Google Chrome .

Update : timer.js does not support microsecond resolution. It just multiplies millisecond count by 1000.

Sorry, there isn't better news!

As of now (February 25, 2013), the quality of high-performance times in Chrome 24 is pretty horrible.

var old = 0; 

for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    console.log(t + ' += ' +d); 
}

outputs something like this:

609689.000000013 += 609689.000000013
609689.9999999441 += 0.9999999310821295
609689.9999999441 += 0
609689.9999999441 += 0
609689.9999999441 += 0
609690.9999999916 += 1.0000000474974513
609690.9999999916 += 0
609690.9999999916 += 0
609691.9999999227 += 0.9999999310821295
609691.9999999227 += 0

Which shows that

1) Sampling happens rarely

2) The precision is still around 1ms, not in the microsecond range.

Node.js has a high resolution timer, too.

process.hrtime()

See the documentation for details

Because this comes up when searching for JavaScript and high resolution timer, it is worth noting that window.performance.now is now functional (at least in Google Chrome v. 26) and provides ~microsecond resolution.

var d = 0;
var p = window.performance;
for(var i=0; i<10; i++) {
    d = -1 * (p.now() - p.now());
    console.log(d*1000);
}

It gave me this (in microseconds):

5.0000089686363935
3.9999722503125668
1.00000761449337
1.00000761449337
1.00000761449337
1.9999861251562834
1.9999861251562834
1.00000761449337
1.00000761449337
0.9999785106629133

I ran some stats on a few sets of 10k+ results. Minimum is about 1 microsecond, and the mean is about 1.25 microseconds on my machine ( MacBook Air ). Occasionally there are high outliers at the 100+ microsecond mark, but there were frequent results above 10 microseconds.

So the high-resolution timer is now capable of timing subtraction at microsecond resolution.

Here's what your code should look like in JavaScript, so it doesn't block the user interface and doesn't use window.requestAnimationFrame which does not work cross-browser.

/* Called once, preload essential resources here. */
function init() {}

/* Updates game/animation state */
function update(time) {}

/* Updates screen graphics  */
function render(time) {}

window.onload = function()
{
    var time;
    var done = false;

    init();
    // Using setTimeout passing zero makes the animate function run
    // as soon as possible, but yielding first.
    setTimeout(animate, 0);

    function animate () {
        time = new Date();
        update(time);
        render(time);
        if (!done) {
            setTimeout(animate, 0);
        }
    }
}

A problem with this approach is that the animate() may get called more often than the screen gets updated (at 60 Hz it won't update more often than abot every 16 ms), causing extra rendering that never makes it to the screen. That's why you should stick with requestAnimationFrame if possible.

Just now I search for such solution, and found this thread. As I can see, all measurements done incorrect, because nobody know how work console.log function. From my experience (and practice) it take too much time, and even work asynchronously.

Just compare this code samples:

var old = 0; 

for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    console.log(t + ' += ' +d); 
}

and this

var old = 0; 
var out = [];
for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    out.push(t + ' += ' +d)
}
console.log(out);

You can use CSS 3 transforms for simple animations that will get full hardware acceleration and will run silky smooth on most modern browsers... If you're expecting smooth animations on WebGL you're pretty much out of luck, because there's no submillisecond precision on JavaScript.

Currently, there's a lot of progress on enabling game related technologies for the web (see for example the mouse lock draft which is being actively developed our the fullscreen API ... Maybe you can start the movement for microsecond precision timers in JavaScript ;-)

Besides the already provided excellent answers, you may also have a look at marky , a

high-resolution JavaScript timer based on performance.mark/measure (461 bytes min+gz)

It also includes dev tools visualizations and is tested in a broad range of browsers.

Node supports nanoseconds via process.hrtime.bigint() , added in v10.7.0.

From the documentation :

The bigint version of the process.hrtime() method returning the current high-resolution real time in nanoseconds as a bigint .

Unlike process.hrtime(), it does not support an additional time argument since the difference can just be computed directly by subtraction of the two bigints.

 import { hrtime } from 'process'; const start = hrtime.bigint(); // 191051479007711n setTimeout(() => { const end = hrtime.bigint(); // 191052633396993n console.log(`Benchmark took ${end - start} nanoseconds`); // Benchmark took 1154389282 nanoseconds }, 1000);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM