Reputation: 59691
I've noticed that if I call the JavaScript setTimeout()
function with a 1 minute delay, and then change my system time to 5 minutes in the past, the callback function will trigger in 6 minutes.
I did this because I wanted to see what happens during a daylight savings change to the system clock.
My webpage calls setTimeout()
to automatically refresh the page every 5 seconds, and if daylight savings were to occur, then the page information would freeze for an hour. Is there a workaround which respects the delay even when such rare changes to the clock occur?
I am updating the page using Ajax, I don't want to refresh the entire page.
Upvotes: 22
Views: 4906
Reputation: 641
What I will do is change from opening a new Ajax request every 5 seconds to using 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.io.
There are other options, like CometD or Ape.
Upvotes: 4
Reputation: 426
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:
setTimeout()
, setInterval()
).My solution: use some event from user interaction (i.e. mousemove
, mouseover
, 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.
Upvotes: 2
Reputation: 1296
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);
});
}
Upvotes: 1
Reputation: 26183
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.
Upvotes: 4
Reputation: 169541
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.
Upvotes: 0
Reputation: 3914
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.
Upvotes: 0