JavaScript中的单调递增时间?

29

如何在JavaScript中获得单调递增的时间?我希望有类似于Java的System.nanoTime()的方法。

Date()显然不能用,因为它会受到系统时间更改的影响。

换句话说,我希望对于任何a和b,都满足a <= b,并且时间单调递增:

a = myIncreasingTime.getMilliseconds();
...
// some time later, maybe seconds, maybe days
b = myIncreasingTime.getMilliseconds();

即使在使用 Date() 中的 UTC 函数时,它仅会返回其认为是正确时间的值。但如果有人将时间设置回去,下一次调用 Date() 可能会返回更小的值。而 System.nanoTime() 不受此限制(至少在系统重新启动之前如此)。

修改:[2012-02-26:这并不意味着要影响原始问题,该问题有一个赏金]

我不关心“墙上时间”,我只关心具有一定准确度的已经过去的时间,Date() 无法提供此功能。


W3Schools 展示了类似的内容。 - undefined
什么是会话?在哪里运行这段代码(浏览器、服务器)? - gblazex
抱歉,我的措辞不太好。希望我刚刚添加的修改更清晰明了。 - danorton
1
@xyu:w3schools并不是一个好的网站 - http://w3fools.com - 他们向setTimeout传递字符串这一事实就足以证明了。 - ThiefMaster
@ThiefMaster 是的,我知道。但是我的意思是使用setTimeout增加变量的想法。 - undefined
我已经在我的原始查询中添加了准确性要求。由于有其他人引入了赏金,我注意到我的要求并不意味着影响赏金。 - danorton
4个回答

25

你可以使用window.performance.now() - 自Firefox 15以来可用,和window.performance.webkitNow() - Chrome 20以后也可用。

var a = window.performance.now();
//...
var delay = window.performance.now() - a;

此函数在IE10中也可用。 - danorton
Web Worker 使用不同的时间起点。https://developer.mozilla.org/zh-CN/docs/Web/API/DOMHighResTimeStamp#The_time_origin - jiafeng fu
1
有点警告,这似乎并不是我所希望的单调性(POSIX单调时钟)。具体来说,它没有考虑在睡眠模式下花费的时间。 - Adrian Günter
它应该考虑到睡眠模式下的时间,但浏览器中存在错误。请参见此说明 - Marv-CZ

7

您可以使用Date()Date.now()来强制其单调(但不准确)。以下是简要的示意图,未经测试:

var offset = 0;
var seen = 0;
function time() {
  var t = Date.now();
  if (t < seen) {
    offset += (seen - t);
  }
  seen = t;
  return t + offset;
}

如果系统时钟在某一时刻被设置回去,那么看起来就好像没有时间流逝(并且包含该时间间隔的经过时间将不正确),但至少您不会出现负增量。如果没有任何回退,则此函数返回与 Date.now() 相同的值。
例如,如果您正在编写游戏模拟循环,则这可能是一个合适的解决方案,因为会非常频繁地调用 time() — 最大误差是回退次数乘以调用之间的间隔。如果您的应用程序自然而然地不这样做,您可以显式地在 setInterval 上调用它,以保持精度,但代价是一些 CPU 时间。
此外,时钟可能会向前调整,这不会阻止单调性,但可能会产生同样不良的影响(例如,游戏花费太长时间一次性赶上其模拟)。但是,这与机器已经睡眠了一段时间不是特别可区分。如果需要这样的保护,只需更改旁边的条件,并对可接受进展设置恒定阈值即可。
if (t > seen + leapForwardMaximum) {
  offset += (seen - t) + leapForwardMaximum;
}

我建议将leapForwardMaximum设置为1000毫秒以上,因为例如Chrome(如果我没记错的话)会限制后台标签页中的定时器触发频率不超过每秒一次。

是的,很好的观点,并且回答了我最初的问题。我已经修改了我的原始问题以添加准确性要求。 - danorton
1
一种有点显而易见的评论,对于原始问题的提问方式并不构成问题,但对于某些人可能仍然很重要:此解决方案无法处理用户将时钟向前设置的情况,即它无法防止返回时间的大正跳跃。 - Markus A.
@MarkusA。这似乎值得讨论,所以我添加了一个相关的部分。 - Kevin Reid

4

Javascript本身没有任何功能来访问nanoTime。您可以加载一个Java小程序来获取该信息,例如benchmark.js已经完成了这个操作。也许@mathias能够解释一下他们是如何做到的...


1

Firefox 提供了 setTimeout 的 "delay" 参数... 这是实现单调递增时间计数器的方法之一。

var time = 0;

setTimeout(function x(actualLateness) {
  setTimeout(x, 0);
  time += actualLateness;
}, 0);

2
setTimeout没有最大延迟的保证。该参数仅指定最小值。使用setTimeout无法确定实际经过的时间。 - danorton
请注意:在 Gecko 13(Firefox 13.0 / Thunderbird 13.0)之前,Gecko 会向回调函数传递一个额外的参数,表示超时的“实际延迟”时间(以毫秒为单位)。现在不再传递此非标准参数,我已经更新了我的答案。 - 4esn0k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接