JavaScript setTimeout и изменения в системном времени вызывают проблемы
Я заметил, что если я установлю setTimeout
в течение 1 минуты в будущем, а затем измените системное время на 5 минут в прошлом, setTimeout
Функция сработает через 6 минут.
Я сделал это потому, что хотел посмотреть, что происходит во время перехода на летнее время на системные часы.
Моя страница JavaScript использует setTimeout
функция автоматического обновления страницы каждые 5 секунд, и в случае перехода на летнее время информация о странице замораживается на час. Есть ли обходной путь?
Изменить: я обновляю страницу с помощью Ajax, я не хочу обновлять всю страницу.
6 ответов
Что я сделаю, так это переключусь с открытия нового Ajax-запроса каждые 5 секунд на использование Comet (длинный опрос). Это лучший вариант, потому что сервер будет отправлять новые результаты в браузер каждый раз, когда они необходимы, это может происходить каждые 5 секунд на стороне сервера или каждый раз, когда доступна новая информация.
Еще лучше было бы использовать веб-сокеты и использовать Comet как запасной вариант. Это легко реализовать с помощью Faye или http://socket.io/.
Есть и другие варианты, например, CometD o Ape.
Использование 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>
Если вам повезет, метаобновление не вызовет таких же проблем.
Другим решением было бы перенести запуск события на сервер через push- запрос сервера. Есть много причин не делать этого, не в последнюю очередь, вы бы централизовали события и сделали их бременем для сервера, но это может считаться последним средством.
Я недавно тоже столкнулся с этой проблемой. В моем случае изменение времени может привести к серьезным финансовым потерям, поэтому я должен был отнестись к этому серьезно.
Немного предыстории:
- IE и Opera правильно обрабатывают системное время (без побочных эффектов для setTimeout, setInterval).
- FF <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;
}
Как вы можете заметить, я перезагружаю таймер и дополнительно синхронизирую время с сервером.
Я обнаружил, что использование 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);
});
}
Вы можете использовать обновление метатега, чтобы сделать это. Этот код обновляет страницу каждые 5 секунд:
<meta http-equiv="refresh" content="5">
Что касается вопроса о javascript, это может быть просто способ, которым браузер обрабатывает setTimeout. Он говорит браузеру выполнить код в установленное время, а затем, когда часы меняются, код все еще выполняется в то время, когда часы изменились? Просто предположение, но я буду помнить об этом в следующий раз, когда буду использовать setTimeout.
Изменить: Хорошо, это не будет работать для AJAX. Снова: Это действительно хороший вопрос. Я уверен, что setTimeout функционирует, как я сказал выше, но вау, это головоломка.
Вы можете выдумать и сделать if (Date in range foo)
проверять. В основном проверьте, находится ли текущая отметка времени в течение 5 секунд после перехода на летнее время, а затем просто обновите страницу немедленно, а не с помощью setTimeout
,
Таким образом, пользователи получают небольшое раздражение при известном изменении времени, но страница не будет зависать в течение часа.