Bla*_*ory 5 javascript settimeout node.js
我的库有基于实时的测试用例,我注意到测试会随机失败并出现 1 毫秒的错误:
expect(received).toBeGreaterThanOrEqual(expected)
Expected: >= 1000
Received: 999
Run Code Online (Sandbox Code Playgroud)
这似乎是由于 setTimeout 过早地调用了该函数。
所以我写了一个单独的测试脚本:
let last = Date.now()
setTimeout(next, 1000)
function next() {
if (Date.now() - last < 1000) process.exit(1)
last = Date.now()
setTimeout(next, 1000)
}
Run Code Online (Sandbox Code Playgroud)
在Node.js v12.19.0、v14.15.3、v15.4.0上,会随机失败:有时脚本可以继续运行,有时脚本很快就会退出。这不仅发生在我的本地计算机上,而且发生在 Github 的 CI 服务器上。
我的问题:这是一个错误吗?或者 setTimeout 的某种预期行为?还是Date.now() - time总是需要加1毫秒?
git-bisect这里使用是罪魁祸首:2c409a285359faae58227da283a4c7e5cd9a2f0c is the first bad commit
commit 2c409a285359faae58227da283a4c7e5cd9a2f0c
Date: Tue Aug 25 13:36:37 2020 -0600
perf_hooks: add idleTime and event loop util
Use uv_metrics_idle_time() to return a high resolution millisecond timer
of the amount of time the event loop has been idle since it was
initialized.
Include performance.eventLoopUtilization() API to handle the math of
calculating the idle and active times. This has been added to prevent
accidental miscalculations of the event loop utilization. Such as not
taking into consideration offsetting nodeTiming.loopStart or timing
differences when being called from a Worker thread.
PR-URL: https://github.com/nodejs/node/pull/34938
Run Code Online (Sandbox Code Playgroud)
这看起来像是一个错误,而不是预期的行为。我会投票反对总是添加 1ms,因为行为不一致。(但是,它是否会早于 1 毫秒?我没有观察到超过 1 毫秒)您可以通过以下方法解决该问题:
const origSetTimeout = setTimeout;
setTimeout = (f, ms, ...args) => {
let o;
const when = Date.now() + ms,
check = ()=> {
let t = when - Date.now();
if (t > 0) Object.assign(o, origSetTimeout(check, t));
else f(...args);
};
return o = origSetTimeout(check, ms);
};
Run Code Online (Sandbox Code Playgroud)
clearTimeout()即使在解决问题时它也将允许。
以下是模拟该问题并每 3 秒更换一次解决方法的浏览器代码:
2c409a285359faae58227da283a4c7e5cd9a2f0c is the first bad commit
commit 2c409a285359faae58227da283a4c7e5cd9a2f0c
Date: Tue Aug 25 13:36:37 2020 -0600
perf_hooks: add idleTime and event loop util
Use uv_metrics_idle_time() to return a high resolution millisecond timer
of the amount of time the event loop has been idle since it was
initialized.
Include performance.eventLoopUtilization() API to handle the math of
calculating the idle and active times. This has been added to prevent
accidental miscalculations of the event loop utilization. Such as not
taking into consideration offsetting nodeTiming.loopStart or timing
differences when being called from a Worker thread.
PR-URL: https://github.com/nodejs/node/pull/34938
Run Code Online (Sandbox Code Playgroud)
下面是一个 Nodejs 代码,它将清楚地显示问题(点中的“p”),并在按 Enter 后应用解决方法。
'use strict';
const ms = 1;
let when = Date.now() + ms;
setTimeout(next, ms);
function next() {
let now = Date.now();
setTimeout(next, ms);
process.stdout.write(now < when ? 'p' : '.');
when = now + ms;
}
process.stdin.on('readable', _=> {
console.log('enabling workaround');
const origSetTimeout = setTimeout;
setTimeout = (f, ms, ...args) => {
let o;
const when = Date.now() + ms,
check = ()=> {
let t = when - Date.now();
if (t > 0) Object.assign(o, origSetTimeout(check, t));
else f(...args);
};
return o = origSetTimeout(check, ms);
};
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
212 次 |
| 最近记录: |