JavaScript setTimeout and changes to system time cause problems

最后都变了- 提交于 2019-12-03 01:31:32
Siedrix

What I will do is change from opening a new Ajax request every 5 seconds to use Comet (long polling). It is a better option because the server will push the new results to the browser every time that they are needed, this can be every 5 seconds on server side or every time new information is available.

Even better would be to use web sockets and have Comet as fall back. This is easy to implement with Faye or socket.oi.

There are other options, like CometD o Ape.

Martin Jespersen

Use setInterval instead of setTimeout and your problems should be resolved :)

Update:

If you really cannot use either setTimeout or setInterval, you could try to have a hidden iframe that loads a simple HTML page that looks something like this:

<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>

If you are lucky the meta refresh won't cause you the same problems.

Another solution would be to move the event firing to the server via server push. There are many reasons not to do this, not the least that you'd centralize the events and make them a burden on the server, but it could be considered a last resort.

I recently encountered this problem too. In my case time change might cause serious financial loses so I had to take it seriously.

Some background:

  • IE and Opera handles systems time change properly (no side effects to 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.

My solution: use some event from user interaction (i.e. mousemove, mouseove, etc) to trigger time change check.

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;

}

As you can notice I restart timer and additionally synchronize time with server.

You can use the meta tag refresh to do this. This code would refresh the page every 5 seconds:

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

As for the javascript question, it might just be the way the browser handles the setTimeout. It tells the browser to execute the code at a set time, and then when the clock changes, the code still executes at the time before the clocked changed? Just a guess, but I will have to keep that in mind next time I use setTimeout.

Edit: Ok, that would not work for ajax Edit Again: This is a really good question. I am sure that setTimeout functions as I said above, but wow, this is a brain teaser.

You can fudge and do an if (Date in range foo) check. Basically check whether the current time stamp is within 5 seconds of daylight saving time and then just refresh the page immediately rather then by using setTimeout.

This way users get a minor annoyance near a known time change but the page won't freeze for an hour.

I found that using window.performance gave me more accurate results and kept things consistent when the system time changes.

I used this as a way to get the current time inside a setInterval tick so that a countdown timer would not be affected by a system time change. I was calculating it based on number of ticks from a given time to attempt at preventing cheating on the timer (getting more time) by changing the system clock

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);
  });
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!