在 Node.js 中单调增加时间

Monotonically increasing time in Node.js

这个问题已经在浏览器 here 中得到了解答,但是 window.performance.now() 在 Node.js 中显然不可用。

一些应用程序需要稳定的时钟,即随时间单调增加的时钟,不受系统时钟漂移的影响。例如,Java 有 System.nanoTime() and C++ has std::chrono::steady_clock。 Node.js有这样的时钟吗?

原来 Node.js 中的等价物是 process.hrtime()。根据文档:

[The time returned from process.hrtime() is] relative to an arbitrary time in the past, and not related to the time of day and therefore not subject to clock drift.


例子

假设我们想要每秒定期调用某个 REST 端点一次,处理其结果并将某些内容打印到日志文件中。考虑端点可能需要一段时间才能响应,例如,从数百毫秒到超过一秒。我们不希望有两个并发请求进行,所以setInterval()不完全满足我们的需求。

一个好的方法是第一次调用我们的函数,执行请求,处理它,然后调用 setTimeout() 并重新安排另一个 运行。但我们希望每秒执行一次,同时考虑到我们发出请求所花费的时间。这是使用稳定时钟的一种方法(这将保证我们不会被系统时钟漂移所愚弄):

function time() {
    const nanos = process.hrtime.bigint();
    return Number(nanos / 1_000_000n);
}

async function run() {
    const startTime = time();

    const response = await doRequest();
    await processResponse(response);

    const endTime = time();
    // wait just the right amount of time so we run once second; 
    // if we took more than one second, run again immediately
    const nextRunInMillis = Math.max(0, 1000 - (endTime - startTime));
    setTimeout(run, nextRunInMillis);
}

run();

我制作了这个辅助函数 time(),它将 process.hrtime.bigint() 返回的值转换为具有毫秒分辨率的时间戳;刚好满足此应用程序的分辨率。

添加了 NodeJS 10.7.0 process.hrtime.bigint()

然后您可以这样做:


function monotimeRef() {
  return process.hrtime.bigint();
}

function monotimeDiff(ref) {
  return Number(process.hrtime.bigint() - ref) / 10**9;
}

演示节点 REPL 中的用法:

// Measure reference time.
> let t0 = monotimeRef();
undefined

[ ... let some time pass ... ]

// Measure time passed since reference time,
// in seconds.
> monotimeDiff(t0)
12.546663115

注:

  • Number()BigInt 转换为常规 Number 类型,允许使用普通除法运算符将纳秒转换为秒。
  • monotimeDiff() returns 壁时间差以纳秒分辨率作为浮点数传递(从转换为 Number before 做师)。
  • 这假设测量的持续时间不会增长 beyond 2^53 ns,实际上只有大约 104 天 (2**53 ns / 10**9 ns/s / 86400.0 s/day = 104.3 day)。