简体   繁体   English

JavaScript 是否提供高分辨率计时器?

[英]Does JavaScript provide a high resolution timer?

Does JavaScript provide a high resolution timer? JavaScript 是否提供高分辨率计时器?

I've written a few game engines from scratch, some in C, some in Java, and some in Flash.我从头开始编写了一些游戏引擎,有些是用 C 语言编写的,有些是用 Java 编写的,有些是用 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.在本机代码 Windows 中,我使用QueryPerformanceCounter()QueryPerformanceFrequency() queryTime()在每个游戏循环中执行queryTime()的作用,并传递更新/渲染时间。 In Java, I use System.nanoTime() .在 Java 中,我使用System.nanoTime()

What's the equivalent in JavaScript? JavaScript 中的等价物是什么? That is, some function like queryTime() which returns a time value with a high degree of accuracy (sub millisecond).也就是说,像queryTime()这样的函数会以高精度(亚毫秒)返回时间值。 From what I've heard the best accuracy you can get in JavaScript is about 15 ms ... which is horrible for animation.据我所知,您在 JavaScript 中可以获得的最佳准确度约为 15 毫秒……这对于动画来说太可怕了。

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 . 这是“高分辨率时间”W3C标准: http//www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp

It allows you to get a sub-millisecond accurate timestamp by calling window.performance.now() . 它允许您通过调用window.performance.now()获得亚毫秒级的准确时间戳。 This returns a time stamp in ms, but it is a float so you still get sub-millisecond resolution. 这将返回以ms为单位的时间戳,但它是一个浮点数,因此您仍然可以获得亚毫秒级的分辨率。

Very old browsers may implement a "prefixed" version of this standard, eg WebKit based browsers used to implement window.performance.webkitNow() 很老的浏览器可能会实现此标准的“前缀”版本,例如用于实现window.performance.webkitNow()的基于WebKit的浏览器

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. 请注意,此getTimestamp()函数不返回表示当前日期/时间的值。 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 . 而不是while (true) / setInterval ,使用递归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. 请参阅@ h3r3的答案 - 这是正确的答案。

Milliseconds are the best you can hope for in JavaScript. 毫秒是您在JavaScript中可以期待的最好的。 And, like you said, it isn't very accurate. 而且,就像你说的那样,它不是很准确。 See Stack Overflow question Microsecond timing in JavaScript . 请参阅Stack Overflow问题JavaScript中的Microsecond时序

timer.js purports to provide up to microsecond resolution, but it is only available for Google Chrome . timer.js旨在提供高达微秒的分辨率,但它仅适用于谷歌浏览器

Update : timer.js does not support microsecond resolution. 更新 :timer.js 支持微秒分辨率。 It just multiplies millisecond count by 1000. 它只是将毫秒计数乘以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. 截至目前(2013年2月25日),Chrome 24的高性能时代质量非常糟糕。

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 1)很少采样

2) The precision is still around 1ms, not in the microsecond range. 2)精度仍然在1ms左右,而不是在微秒范围内。

Node.js has a high resolution timer, too. Node.js也有一个高分辨率计时器。

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. 因为在搜索JavaScript和高分辨率计时器时会出现这种情况,所以值得注意的是window.performance.now现在正在运行(至少在Google Chrome v.26中),并提供〜微秒的分辨率。

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. 我在几组10k +结果上运行了一些统计数据。 Minimum is about 1 microsecond, and the mean is about 1.25 microseconds on my machine ( MacBook Air ). 最小值约为1微秒,我的机器( MacBook Air )的平均值约为1.25微秒。 Occasionally there are high outliers at the 100+ microsecond mark, but there were frequent results above 10 microseconds. 有时在100+微秒标记处存在高异常值,但是频繁的结果超过10微秒。

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. 以下是您的代码在JavaScript中应该是什么样子,因此它不会阻止用户界面,也不会使用不能跨浏览器工作的window.requestAnimationFrame

/* 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. 这种方法的一个问题是animate()可能比屏幕更新更频繁地被调用(在60 Hz时它不会比每16 ms更频繁地更新),从而导致额外的渲染从未进入屏幕。 That's why you should stick with requestAnimationFrame if possible. 这就是为什么你应该坚持使用requestAnimationFrame。

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. 正如我所看到的,所有测量都做错了,因为没有人知道console.log的工作原理。 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. 你可以使用CSS 3转换来获得完全硬件加速的简单动画,并且可以在大多数现代浏览器上运行顺畅...如果你期望在WebGL上运行流畅的动画,你几乎没有运气,因为没有亚毫秒精度在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 ;-) 目前,在为网络启用游戏相关技术方面取得了很多进展(例如,参见鼠标锁定草案,正在积极开发我们的全屏API ......也许你可以在JavaScript中启动微秒精度计时器的运动;-)

Besides the already provided excellent answers, you may also have a look at marky , a 除了已经提供的优秀答案,您还可以看看marky ,a

high-resolution JavaScript timer based on performance.mark/measure (461 bytes min+gz) 基于performance.mark / measure(461字节min + gz)的高分辨率JavaScript计时器

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. Node 通过 v10.7.0 中添加的process.hrtime.bigint()支持纳秒。

From the documentation :文档

The bigint version of the process.hrtime() method returning the current high-resolution real time in nanoseconds as a bigint .bigint版本的process.hrtime()方法返回以纳秒为目前的高解析度实时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.与 process.hrtime() 不同,它不支持额外的时间参数,因为可以通过两个 bigint 相减直接计算差异。

 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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