根据documentation,setTimeout的实际延迟可能比问题要长。你能不能给我指点文件或问题的答案,这可以解释setTimeout的实际延迟是否比所要求的更短?
事情是我遇到了一个很少发生的问题,可以用这种现象来解释。平台:Chrome版本67,NodeJS版本9.8.0。另外,我真的好奇这个说法是否适用于Firefox和其他浏览器?
答案 0 :(得分:4)
您可以通过取两个微时间的增量来测试超时或间隔精度。在我的测试中,定时器加上或minus 几毫秒。运行下面的程序,在您自己的环境中查看结果。
在一个完美的世界中,输出总是显示1000
。输出的方差意味着我们的世界是不完美的
var last = Date.now()
var interval = setInterval(function() {
var now = Date.now()
var delta = now - last
console.log(delta)
last = now
}, 1000)
setTimeout(clearInterval, 10000, interval)
// 1000
// 1003
// 998
// 1002
// 999
// 1007
// 1001
// ...
要显着影响结果,请按运行,切换到另一个标签页,然后在几秒钟后返回此标签页。您会看到去焦点的标签具有极高的差异。
// 1004 <-- start experiment
// 997
// 1000 <-- switch to another tab
// 1533 <-- variance spikes immediately
// 866
// 1033
// 568 <-- switch back to this tab
// 1001 <-- variance restabilizes
// 1000
// 999
我不知道所有在影响JavaScript中超时和间隔的准确性方面起作用的事情,但我也不认为这是一件重要的事情。最终,我们不需要精确度,因为我们可以使用上面的delta技术计算精确的持续时间。
React中的实际示例
下面我们制作一个简单的Timer
组件,天真地使用setInterval
每秒刷新一次计时器的显示...
class Timer extends React.Component {
constructor (props) {
super (props)
this.state = { seconds: 0, timeout: null }
}
componentDidMount () {
this.setState ({
timeout: setInterval (this.tick.bind(this), 1000)
})
}
componentWillUnmount () {
clearTimeout (this.timeout)
}
tick () {
this.setState ({ seconds: this.state.seconds + 1 })
}
render () {
return <div>Naive timer: {this.state.seconds}</div>
}
}
ReactDOM.render
( <Timer />
, document.getElementById ('timer')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="timer"></div>
但是由于JavaScript定时器的不可靠性,我们知道我们的Timer组件最终会显示不正确的值。
当我们在下面实施PreciseTimer
时,我们可以使用delta技术来确保组件始终显示正确的持续时间
class PreciseTimer extends React.Component {
constructor (props) {
super (props)
this.state = { start: Date.now (), seconds: 0, timeout: null }
}
componentDidMount () {
this.setState ({
timeout: setInterval (this.tick.bind(this), 1000)
})
}
componentWillUnmount () {
clearTimeout (this.timeout)
}
tick () {
const delta = Date.now () - this.state.start
this.setState ({ seconds: delta / 1000 })
}
render () {
return <div>Precise timer: {this.state.seconds}</div>
}
}
ReactDOM.render
( <PreciseTimer />
, document.getElementById ('timer')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="timer"></div>
要查看这两个计时器的行为方式的实际差异,请同时启动它们,切换到新选项卡10-15秒,然后切换回此选项卡。幼稚Timer
将受到JavaScript计时器差异的影响,而PreciseTimer
将始终显示正确的持续时间。
答案 1 :(得分:1)
实际上,setTimeout只会在指定的延迟之后将回调推送到事件循环之上,但是因为Javascript是单声道进程,如果阻止事件循环,回调将被延迟。但它可能会缩短或者是使用的代码/ javascript引擎中的错误。
答案 2 :(得分:1)
Javascript的单个执行线程意味着有时候你排队等待将来在x点执行的事情可能实际上是按时执行x +,因为可能没有执行时间帧可用。 然而,事情永远无法在所需时间之前执行,只能在给定时间之前或之后执行
例如,setTimeout(myFunc,100)可以在100毫秒后执行,或者稍微长一点。 您可以在本文中找到有关javascrip的计时器的更多信息:https://johnresig.com/blog/how-javascript-timers-work/
答案 3 :(得分:1)
要完成user633183's answer,这里有一个计时器可确保您的功能从未在给定时间间隔之前运行:
class Timer {
constructor() {
this.last = Date.now();
this.interval = null;
}
start (f, ms) {
// Stop previous timer (if any)
this.stop();
// Start new timer
this.interval = setInterval( () => {
const now = Date.now();
// Don't execute before given time interval
if (now - this.last < ms)
return;
this.last = now;
f();
}, ms);
}
stop () {
if (this.interval)
clearInterval(this.interval);
this.interval = null;
}
}
const timer = new Timer();
timer.start( () => console.log(Date.now()), 1000);