更新:在这里使用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
这似乎是一个bug,而不是预期的行为。我反对总是添加1毫秒,因为行为不一致。(但是,它是否会比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);
};
即使在解决问题时,也可以使用clearTimeout()
。
以下是一个浏览器代码,模拟该问题并每3秒钟交替使用解决方法:
const realOrigSetTimeout = setTimeout;
setTimeout = (func, ms, ...args) => realOrigSetTimeout(func, ms - Math.random(), ...args);
const ms = 200;
let when = Date.now() + ms;
setTimeout(next, ms);
function next() {
let now = Date.now();
setTimeout(next, ms);
console.log(now < when ? 'premature' : 'ok');
when = now + ms;
}
function workAround() {
console.log('Applying 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);
};
setTimeout(_=>{
console.log('Removing workaround');
setTimeout = origSetTimeout;
setTimeout(workAround, 3000);
}, 3000);
}
setTimeout(workAround, 3000);
以下是一个 Node.js 代码,它将清楚地显示问题('p' 在点之间),并在按下回车键后应用解决方法。
'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);
};
});
Date.now()
问题,但是process.hrtime()
更精确地显示了完全相同的问题。 - niry