JavaScript setTimeout и изменения в системном времени вызывают проблемы - PullRequest
20 голосов
/ 03 февраля 2011

Я заметил, что если я установлю setTimeout на 1 минуту в будущем, а затем изменит системное время на 5 минут в прошлом, функция setTimeout сработает через 6 минут.

Я сделал это потому, что хотел посмотреть, что происходит во время перехода на летнее время на системные часы.

Моя веб-страница JavaScript использует функцию setTimeout, чтобы автоматически обновлять страницу каждые 5 секунд, и, если наступит переход на летнее время, информация о странице будет остановлена ​​на час. Есть ли обходной путь?

Редактировать: я обновляю страницу с помощью Ajax, я не хочу обновлять всю страницу.

Ответы [ 6 ]

4 голосов
/ 13 февраля 2011

Я перейду к открытию нового Ajax-запроса каждые 5 секунд, чтобы использовать Comet (длинный опрос).Это лучший вариант, потому что сервер будет отправлять новые результаты в браузер каждый раз, когда они необходимы, это может происходить каждые 5 секунд на стороне сервера или каждый раз, когда доступна новая информация.

Еще лучшеиспользовать веб-сокеты и использовать Comet как отступить.Это легко реализовать с помощью Faye или socket.oi .

Существуют и другие варианты, например CometD o Ape.

4 голосов
/ 03 февраля 2011

Используйте 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 голос
/ 20 февраля 2011

Я недавно тоже столкнулся с этой проблемой. В моем случае изменение времени может привести к серьезным финансовым потерям, поэтому я должен был отнестись к этому серьезно.

Некоторый фон:

  • IE и Opera правильно обрабатывают системное время (без побочных эффектов для setTimeout, setInterval).
  • FF <4.0 и все браузеры на основе Webkit не справляются с изменением времени на прошлое (в точности, как вы описали). Примечание: таймеры в Chrome, однако, останавливаются только через некоторое время ~ 2-60 с. </li>

Мое решение: использовать какое-либо событие из взаимодействия с пользователем (например, 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;

}

Как вы можете заметить, я перезагружаю таймер и дополнительно синхронизирую время с сервером.

0 голосов
/ 23 марта 2018

Я обнаружил, что использование 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 голосов
/ 13 февраля 2011

Вы можете выдумать и сделать проверку if (Date in range foo).В основном проверьте, находится ли текущая отметка времени в течение 5 секунд после перехода на летнее время, а затем просто обновите страницу немедленно, а не с помощью setTimeout.

Таким образом, пользователи получают небольшое раздражение рядом с известным изменением времени, ностраница не будет зависать в течение часа.

0 голосов
/ 03 февраля 2011

Вы можете использовать обновление метатега, чтобы сделать это. Этот код обновляет страницу каждые 5 секунд:

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

Что касается вопроса о javascript, возможно, браузер обрабатывает setTimeout. Он говорит браузеру выполнить код в установленное время, а затем, когда часы меняются, код все еще выполняется в то время, когда часы изменились? Просто предположение, но я буду помнить об этом в следующий раз, когда буду использовать setTimeout.

Edit: Хорошо, это не будет работать для AJAX Снова отредактируйте: это действительно хороший вопрос. Я уверен, что setTimeout функционирует, как я уже сказал выше, но вау, это головоломка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...