JavaScript setTimeout和系统时间的更改会导致问题

时间:2011-02-02 22:54:29

标签: javascript settimeout system-clock

我注意到如果我将来将setTimeout设置为1分钟,然后将系统时间更改为过去5分钟,则setTimeout功能将在6分钟内触发。

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

我的JavaScript网页使用setTimeout功能每5秒自动刷新一次页面,如果夏令时发生,那么页面信息会冻结一小时。有解决方法吗?

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

6 个答案:

答案 0 :(得分: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>

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

另一种解决方案是通过server push将事件触发移动到服务器。有很多理由不这样做,尤其是你集中了事件并使它们成为服务器的负担,但它可以被认为是最后的手段。

答案 1 :(得分:4)

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

更好的方法是使用网络套接字并让Comet作为后退。这可以通过Fayesocket.oi轻松实现。

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

答案 2 :(得分:1)

我最近也遇到过这个问题。在我看来,时间的变化可能导致严重的财务损失,所以我不得不认真对待。

一些背景知识:

  • IE和Opera正确处理系统时间变化(对setTimeout,setInterval没有副作用)。
  • FF&lt; 4.0并且所有基于Webkit的浏览器都无法处理到过去的更改时间(正如您所描述的那样)。注意:Chrome中的计时器仅在2-60秒后停止。

我的解决方案:使用来自用户交互的一些事件(即mousemove,mouseove等)来触发时间更改检查。

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;

}

您可以注意到我重启计时器并另外与服务器同步时间。

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

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

这样,用户可以在已知的时间变化附近获得轻微的烦恼,但页面不会冻结一小时。

答案 5 :(得分:0)

我发现使用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);
  });
}