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,

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

Другие вопросы по тегам