在 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
)。
这个问题已经在浏览器 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
)。