JavaScript 定时器(setTimeout)不受系统时间变化影响

问题描述 投票:0回答:6

我注意到,如果我延迟 1 分钟调用 JavaScript

setTimeout()
函数,然后将系统时间更改为过去 5 分钟,则回调函数将在 6 分钟后触发。

我这样做是因为我想看看夏令时系统时钟更改期间会发生什么。

我的网页调用

setTimeout()
每5秒自动刷新一次页面,如果发生夏令时,那么页面信息会冻结一个小时。即使时钟发生如此罕见的变化,是否有一种解决方法可以尊重延迟?

我正在使用 Ajax 更新页面,我不想刷新整个页面。

javascript settimeout system-clock
6个回答
4
投票

我要做的就是从每 5 秒打开一个新的 Ajax 请求更改为使用 Comet(长轮询)。这是一个更好的选择,因为服务器每次需要时都会将新结果推送到浏览器,这可以是服务器端每 5 秒一次或每次有新信息可用时。

更好的是使用网络套接字并让 Comet 作为后备。使用 Fayesocket.io 可以轻松实现。

还有其他选择,例如 CometD 或 Ape。


4
投票

使用

setInterval
代替
setTimeout
,您的问题应该得到解决:)

更新:

如果您确实无法使用 setTimeout 或 setInterval,您可以尝试使用隐藏的

iframe
加载一个简单的 HTML 页面,如下所示:

<html>
    <head>
        <meta http-equiv="refresh" content="300"/>
        <script>
            document.domain='same as parent page';
            top.ajax_function_you_wish_to_trigger();
        </script>
    </head>
    <body>
    </body>
</html>

如果你幸运的话,元刷新不会给你带来同样的问题。

另一种解决方案是通过服务器推送将事件触发移至服务器。不这样做的原因有很多,尤其是您会集中事件并使它们成为服务器的负担,但它可以被视为最后的手段。


2
投票

我最近也遇到这个问题了。就我而言,时间变化可能会导致严重的经济损失,所以我必须认真对待它。

一些背景:

  • IE 和 Opera 正确处理系统时间更改(对
    setTimeout()
    setInterval()
    没有副作用)。
  • FF< 4.0 and all Webkit based browser fails to handle changing time to the past (exactly as you described). Note: timers in Chrome however stops only after some time ~2-60s.

我的解决方案:使用用户交互中的一些事件(即

mousemove
mouseover
等)来触发时间更改检查。

Clock.prototype.checkLocalTime = function(){
  var diff = Date.now()- this.lastTimestamp;
  if ( diff < 0 || diff > 20000) { // if time shifted back or more than 20s to the future
    console.warn('System time changed! By ' + diff + 'ms' );
    this.restartTimer();
    this.getServerTime();
  }
  this.lastTimestamp = time;
}

正如您所注意到的,我重新启动了计时器并另外与服务器同步时间。


1
投票

我发现使用 window.performance 给了我更准确的结果,并在系统时间变化时保持一致。

我用它来获取 setInterval 刻度内的当前时间,这样倒计时器就不会受到系统时间更改的影响。我根据给定时间的滴答数来计算它,以尝试通过更改系统时钟来防止计时器作弊(获得更多时间)

getCurrentTime = () => {
  return window.performance ?
    performance.timing.navigationStart + performance.now() : Date.now();
}

tick() {
  let now = this.getCurrentTime();

  if (this.calibrateTime) { //calibrate with a given server time
    now -= this.timeCalibration;
  }

  const elapsedSeconds = (now - this.startedAt) / 1000;

  if (elapsedSeconds > this.props.timeLimit + this.props.submitBuffer) {
    clearInterval(this.interval);
    this.props.onTimeUp();
  }

  let secondsRemaining = this.props.timeLimit - Math.floor(elapsedSeconds);
  if (secondsRemaining < 0) secondsRemaining = 0;

}

startTimer() {
  if (!this.startedAt) {
    this.startedAt = Date.now();
  }

  this.setState({ started: true }, () => {
    this.interval = setInterval(this.tick.bind(this), 500);
  });
}

0
投票

您可以使用元标记刷新来执行此操作。此代码将每 5 秒刷新一次页面:

<meta http-equiv="refresh" content="5"> 

至于javascript问题,这可能只是浏览器处理setTimeout的方式。它告诉浏览器在设定的时间执行代码,然后当时钟改变时,代码仍然在时钟改变之前的时间执行?只是猜测,但下次使用 setTimeout 时我必须记住这一点。

编辑:好吧,这对 ajax 不起作用 再次编辑:这是一个非常好的问题。我确信 setTimeout 的功能正如我上面所说的那样,但是哇,这是一个脑筋急转弯。


0
投票

您可以捏造并进行

if (Date in range foo)
检查。基本上检查当前时间戳是否在夏令时的 5 秒内,然后立即刷新页面,而不是使用
setTimeout

这样,用户在接近已知时间变化时会感到一些小烦恼,但页面不会冻结一个小时。

© www.soinside.com 2019 - 2024. All rights reserved.