setTimeout的实际延迟时间是否可以缩短?

wan*_*len 0 javascript time node.js

根据文档,setTimeout的实际延迟可能比要求的时间更长。您能指出文件或问题的答案吗,这可能解释setTimeout的实际延迟是否比要求的短?

我遇到的是很少发生的问题,可以用这种现象来解释。平台:Chrome版本67,NodeJS版本9.8.0。另外,我真的很好奇这句话对Firefox和其他浏览器是否正确?

Tha*_*you 5

您可以通过获取两个微时间的增量来测试超时或间隔准确性。在我的测试中,计时器为正负几毫秒。运行以下程序以在您自己的环境中查看结果。

在一个完美的世界中,输出将始终显示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
// ... 
Run Code Online (Sandbox Code Playgroud)

要显着影响结果,请按运行,切换到另一个选项卡,然后在几秒钟后返回此选项卡。您会看到散焦的标签差异很大。

// 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
Run Code Online (Sandbox Code Playgroud)

我不了解影响JavaScript超时和间隔准确性的所有因素,但是我也不认为这是重要的事情。最终,我们不需要准确性,因为我们可以使用上述增量技术来计算精确的持续时间。


React中的一个实际例子

下面我们创建一个简单的Timer组件,它天真地用于setInterval每秒刷新一次计时器的显示...

// 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
Run Code Online (Sandbox Code Playgroud)
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')
  )
Run Code Online (Sandbox Code Playgroud)

但是由于JavaScript计时器的不可靠特性,我们知道我们的Timer组件最终将显示错误的值。

当我们实现PreciseTimer以下内容时,我们可以使用增量技术来确保组件始终显示正确的持续时间

<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>
Run Code Online (Sandbox Code Playgroud)
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')
  )
Run Code Online (Sandbox Code Playgroud)

要查看这两个计时器在行为上的实际差异,请启动两个计时器,切换到新标签10-15秒钟,然后再切换回该标签。天真Timer会受到JavaScript计时器变化的影响,而PreciseTimer始终会显示正确的持续时间。