Получите разные выходные данные в узле и браузере (около setTimeout) [дублировать]

UPDATE на студии Android AVD:

  1. открыть AVD-менеджер
  2. создать / отредактировать AVD
  3. щелкнуть расширенные настройки
  4. ] выберите предпочтительную настройку подключения

Нет микроволн или лифтов:)

15
задан Siguza 30 April 2015 в 15:53
поделиться

5 ответов

Почему это неточно?

Поскольку вы используете setTimeout() или setInterval(). Им нельзя доверять , для них нет гарантий точности. Им разрешено произвольно запаздывать , и они не сохраняют постоянный темп, но имеют тенденцию дрейфовать (как вы заметили).

Как создать точный таймер?

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

Для простого таймера или часов четко отслеживайте разницу во времени:

var start = Date.now();
setInterval(function() {
    var delta = Date.now() - start; // milliseconds elapsed since start
    …
    output(Math.floor(delta / 1000)); // in seconds
    // alternatively just show wall clock time:
    output(new Date().toUTCString());
}, 1000); // update about every second

Теперь у этого есть проблема, возможно, прыгающих значений. Когда интервал задерживается бит и выполняет ваш обратный вызов после 990, 1993, 2996, 3999, 5002 миллисекунд, вы увидите второй счет 0, 1, 2, 3, 5 (!). Поэтому было бы желательно обновлять чаще, примерно каждые 100 мс, чтобы избежать таких переходов.

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

var interval = 1000; // ms
var expected = Date.now() + interval;
setTimeout(step, interval);
function step() {
    var dt = Date.now() - expected; // the drift (positive for overshooting)
    if (dt > interval) {
        // something really bad happened. Maybe the browser (tab) was inactive?
        // possibly special handling to avoid futile "catch up" run
    }
    … // do what is to be done

    expected += interval;
    setTimeout(step, Math.max(0, interval - dt)); // take into account drift
}
60
ответ дан Community 16 August 2018 в 03:29
поделиться
  • 1
    Небольшое объяснение для второго кода таймера было бы действительно полезным – Hassaan 2 March 2016 в 13:23
  • 2
    @ M.Hassaan: Я добавил предложение, но я не совсем уверен, что вы ищете. Код использует довольно описательные имена и уже давно прокомментирован. Требуется ли какая-либо конкретная часть для уточнения? – Bergi 2 March 2016 в 15:55
  • 3
    Это заняло довольно много времени, чтобы понять, но как только я это сделал, это было довольно просто. Это предложение было приятным дополнением! – Hassaan 2 March 2016 в 21:13
  • 4
    Это зависит от того, что делает ваш таймер. В большинстве случаев есть лучший способ, чем выполнение step несколько раз подряд. Если вы используете стратегию разницы (вместо счетчика), которую вы должны независимо от того, настроен ли таймер самостоятельно или нет, вам потребуется всего один шаг, и он автоматически набирается. – Bergi 3 March 2016 в 10:03
  • 5
    @ nem035 Ну что ж, вопросов достаточно, на которые я еще не ответил :-) – Bergi 26 August 2016 в 01:22

Я согласен с Берги в использовании Date, но его решение было немного излишним для моего использования. Я просто хотел, чтобы мои анимированные часы (цифровые и аналоговые SVG) обновлялись на втором и не переполнялись или выполнялись, создавая очевидные скачки в обновлениях часов. Вот фрагмент кода, который я ввел в мои функции обновления часов:

    var milliseconds = now.getMilliseconds();
    var newTimeout = 1000 - milliseconds;
    this.timeoutVariable = setTimeout((function(thisObj) { return function() { thisObj.update(); } })(this), newTimeout);

Он просто вычисляет время дельта до следующего даже второго и устанавливает тайм-аут этой дельта. Это синхронизирует все мои объекты часов со вторым. Надеюсь, это полезно.

1
ответ дан agent-p 16 August 2018 в 03:29
поделиться

Я просто опишу ответ Берги (в частности, вторую часть) немного, потому что мне очень понравилось, как это было сделано, но я хочу, чтобы опция останавливала таймер после его запуска ( как clearInterval() почти). Sooo ... Я завернул его в конструктор, чтобы мы могли делать с ним «объективные» вещи.

1. Конструктор

Хорошо, поэтому вы копируете / вставляете это ...

/**
 * Self-adjusting interval to account for drifting
 * 
 * @param {function} workFunc  Callback containing the work to be done
 *                             for each interval
 * @param {int}      interval  Interval speed (in milliseconds) - This 
 * @param {function} errorFunc (Optional) Callback to run if the drift
 *                             exceeds interval
 */
function AdjustingInterval(workFunc, interval, errorFunc) {
    var that = this;
    var expected, timeout;
    this.interval = interval;

    this.start = function() {
        expected = Date.now() + this.interval;
        timeout = setTimeout(step, this.interval);
    }

    this.stop = function() {
        clearTimeout(timeout);
    }

    function step() {
        var drift = Date.now() - expected;
        if (drift > that.interval) {
            // You could have some default stuff here too...
            if (errorFunc) errorFunc();
        }
        workFunc();
        expected += that.interval;
        timeout = setTimeout(step, Math.max(0, that.interval-drift));
    }
}

2. Мгновенное действие

Скажите, что делать и все это ...

// For testing purposes, we'll just increment
// this and send it out to the console.
var justSomeNumber = 0;

// Define the work to be done
var doWork = function() {
    console.log(++justSomeNumber);
};

// Define what to do if something goes wrong
var doError = function() {
    console.warn('The drift exceeded the interval.');
};

// (The third argument is optional)
var ticker = new AdjustingInterval(doWork, 1000, doError);

3. Тогда сделайте ... stuff

// You can start or stop your timer at will
ticker.start();
ticker.stop();

// You can also change the interval while it's in progress
ticker.interval = 99;

Я имею в виду, это работает для меня в любом случае. Если есть лучший способ, знаете ли.

3
ответ дан Leon Williams 16 August 2018 в 03:29
поделиться
  • 1
    Вместо использования логического флага вы также можете хранить идентификатор интервала каждый раз и использовать clearTimeout на последнем. – Bergi 2 June 2017 в 22:26
  • 2
    Хорошо, я думаю, что сделал это. Я отредактировал свой ответ. Кажется, работает. Большое спасибо. – Leon Williams 2 June 2017 в 23:41

Не намного точнее, чем это.

var seconds = new Date().getTime(), last = seconds,

intrvl = setInterval(function() {
    var now = new Date().getTime();

    if(now - last > 5){
        if(confirm("Delay registered, terminate?")){
            clearInterval(intrvl);
            return;
        }
    }

    last = now;
    timer.innerHTML = now - seconds;

}, 333);

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

3
ответ дан php_nub_qq 16 August 2018 в 03:29
поделиться
  • 1
    Все становится веселым, когда пользователь начинает менять время работы ОС. – eithed 30 April 2015 в 15:39
  • 2
    @eithedog хорошо не получается лучше этого. На самом деле, держись! – php_nub_qq 30 April 2015 в 15:40
  • 3
    @eithedog там, все хорошо сейчас. – php_nub_qq 30 April 2015 в 15:41
  • 4
    Если вам не нужны 2-секундные паузы, а затем отображаемый номер, например, от 10 до 12, сделайте это обновление более часто (скажем, 100 мс вместо 1000) – James 30 April 2015 в 15:42
  • 5
    Я бы снял предупреждение, потому что, если бы я использовал таймер, просто ради любопытства, я бы перешел и изменил текущее системное время, потому что это похоже на то, что вы косвенно говорите мне «да, что произойдет, если вы это сделаете ?!?!?! что & Quot; imgs.xkcd.com/comics/the_difference.png – briosheje 30 April 2015 в 15:50
1
ответ дан V. Rubinetti 29 October 2018 в 09:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: