Монотонное увеличение времени в JavaScript?

Какой лучший способ получить монотонно увеличивающееся время в JavaScript? Я надеюсь на что-то вроде Javas System.nanoTime().

Date() явно не работает, так как изменяется его системное время.

Другими словами, я бы хотел, чтобы для <= b, всегда:

a = myIncreasingTime.getMilliseconds();
...
// some time later, maybe seconds, maybe days
b = myIncreasingTime.getMilliseconds();

В лучшем случае, даже при использовании функций UTC в Date(), он вернет то, что, по его мнению, является правильным временем, но если кто-то устанавливает время назад, следующий вызов Date() может вернуть меньшее значение. System.nanoTime() не страдает от этого ограничения (по крайней мере, пока система не перезагрузится).

Модификация: [2012-02-26: не предназначено для воздействия на исходный вопрос, который имеет награду]

Мне не интересно знать "время стены", мне интересно узнать прошедшее время с некоторой точностью, которую Date() не может обеспечить.

Ответы

Ответ 1

Вы можете использовать window.performance.now() - начиная с Firefox 15 и window.performance.webkitNow() - Chrome 20]

var a = window.performance.now();
//...
var delay = window.performance.now() - a;

Ответ 2

Вы можете обернуть Date() или Date.now(), чтобы заставить его быть монотонным (но неточным). Эскиз, непроверенный:

var offset = 0;
var seen = 0;
function time() {
  var t = Date.now();
  if (t < seen) {
    offset += (seen - t);
  }
  seen = t;
  return t + offset;
}

Если системные часы будут установлены в данный момент, тогда будет казаться, что время не прошло (и истекшее время, содержащее этот интервал, будет неверным), но вы, по крайней мере, не будете иметь отрицательные дельта. Если нет отклонений, это возвращает то же значение, что и Date.now().

Это может быть подходящим решением, если вы пишете цикл моделирования игр, например, где time() вызывается очень часто - максимальная ошибка - это количество обратных интервалов между интервалами между вызовами. Если ваше приложение, естественно, этого не делает, вы можете явно называть его setInterval, скажем (предполагая, что он не заблокирован системными часами), чтобы ваша точность зависела от некоторого времени процессора.


Также возможно, что часы будут установлены вперед, что не предотвратит монотонность, но может иметь одинаково нежелательные эффекты (например, слишком продолжительная игра, пытаясь догнать ее симуляцию сразу). Однако это не особенно отличается от того, что машина некоторое время спала. Если такая защита желательна, это просто означает изменение условия рядом с существующим, с постоянным порогом для приемлемого прогресса:

if (t > seen + leapForwardMaximum) {
  offset += (seen - t) + leapForwardMaximum;
}

Я бы предположил, что leapForwardMaximum должен быть установлен более чем на 1000 мс, потому что, например, Chrome (если я правильно помню) отключает таймеры на вкладках фона для запуска не более одного раза в секунду.

Ответ 3

Javascript сам по себе не имеет функций для доступа к nanoTime. Вы можете загрузить java-апплет для aqcuire, чтобы эта информация, например benchmark.js. Возможно, @mathias может пролить свет на то, что они там сделали...

Ответ 4

Firefox предоставляет аргумент "delay" для setTimeout... это один из способов реализации монотонно увеличенного счетчика времени.

var time = 0;

setTimeout(function x(actualLateness) {
  setTimeout(x, 0);
  time += actualLateness;
}, 0);

Ответ 5

В WEB существует множество сервисов времени. Можно ли использовать один из них? Конечно, из-за времени загрузки будет некоторый разрыв, но этот разрыв может быть определен в некоторой степени с помощью Date() - объекта.