Подтвердить что ты не робот

SetInterval время медленно отходит от точного

Кажется, что когда я setInterval за 1000 мс, он фактически запускает функцию каждые 1001 мс или около того. Это приводит к медленному временному дрейфу, чем дольше его работа.

var start;
var f = function() {
    if (!start) start = new Date().getTime();
    var diff = new Date().getTime() - start;
    var drift = diff % 1000;
    $('<li>').text(drift + "ms").appendTo('#results');
};

setInterval(f, 1000);

При запуске это немедленно показывает неточность.

  • 0ms
  • 1мс
  • 2мс
  • 3ms
  • 4ms
  • 5ms
  • 5ms
  • 7мс
  • 8ms
  • 9ms
  • 9ms
  • 10мс

Посмотрите сами: http://jsfiddle.net/zryNf/

Итак, есть ли более точный способ сохранить время? или способ сделать setInterval вести себя с большей точностью?

4b9b3361

Ответ 1

Думаю, я, возможно, понял решение. Я полагал, что если вы можете измерить его, вы можете компенсировать это, правильно?

http://jsfiddle.net/zryNf/9/

var start;
var nextAt;

var f = function() {
    if (!start) {
        start = new Date().getTime();
        nextAt = start;
    }
    nextAt += 1000;

    var drift = (new Date().getTime() - start) % 1000;    
    $('<li>').text(drift + "ms").appendTo('#results');

    setTimeout(f, nextAt - new Date().getTime());
};

f();
Результат

немного изменился, но вот недавний прогон:

0ms
7ms
2ms
1ms
1ms
1ms
2ms
1ms
1ms
1ms

Итак, если он получает вызов на 1 мс, 2 мс или даже на 10 мс позже, он должен заплатить следующий вызов, чтобы компенсировать это. Пока неточность выполняется только за звонок, но часы никогда не должны терять время, тогда это должно работать хорошо.


И теперь я завернул глобальную функцию accurateInterval, которая заменяет замену на setInterval. https://gist.github.com/1d99b3cd81d610ac7351

Ответ 2

с небольшим количеством googleing, вы увидите, что setInterval и settimeout оба не будут выполнять код в указанное вами время. с setInterval(f,1000); он будет ждать ПО МЕНЬШЕЙ 1000MS до его выполнения, он НЕ будет ждать точно 1000MS. Другие процессы также ждут своей очереди использовать процессор, что вызывает задержки. Если вам нужен точный таймер, который раз в 1 секунду. Я бы использовал более короткий интервал, например 50MS, и сравнил его со временем начала. Я бы не пошел под 50MS, хотя из-за того, что браузеры имеют минимальный интервал

вот несколько ссылок:

"Чтобы понять, как таймеры работают внутри, существует одна важная концепция, которую нужно изучить: таймерная задержка не гарантируется. Поскольку все JavaScript в браузере выполняется в одном потоке асинхронных событий (таких как щелчки мыши и таймеры) выполняются только при открытии в исполнении. Это лучше всего продемонстрировать с помощью диаграммы, например, следующим образом:" взято из: http://css.dzone.com/news/how-javascript-timers-work

"Chrome и Chromium обеспечивают интервал, который составляет в среднем чуть более 41 миллисекунды, достаточная разница для второго такта будет заметно медленнее, чем через минуту. Safari поставляется чуть ниже 41 мс, работая лучше, чем Chrome, но все же не очень. Я взял эти показания под Windows XP, но Chrome на самом деле оказался хуже в Windows 7, где интервал составлял около 46 мс". взято из: http://www.goat1000.com/2011/03/23/how-accurate-is-window.setinterval.html

Ответ 3

Вот еще один интервал автокоррекции. Интервал устанавливается на более короткий период времени, а затем он ждет, пока он не по крайней мере через секунду не загорится. Он не всегда срабатывает ровно через 1000 мс (кажется, колеблется от 0 до 6 мс), но он автокорректируется и не дрейфует.

EDIT: Обновлено для использования вызова setTimeout вместо setInterval, иначе он может сделать что-то нечетное после 1000 или так итераций.

var start, tick = 0;
var f = function() {
    if (!start) start = new Date().getTime();
    var now = new Date().getTime();
    if (now < start + tick*1000) {
        setTimeout(f, 0);
    } else {
        tick++;
        var diff = now - start;
        var drift = diff % 1000;
        $('<li>').text(drift + "ms").appendTo('#results');
        setTimeout(f, 990);
    }
};

setTimeout(f, 990);

Запустить демо

Ответ 4

Я не вижу дрейфа, почти такого, как ваш script сообщает:
http://jsfiddle.net/hqmLg/1/

Я покидаю этот script запуск. Прямо сейчас (Chrome, Win 7) Я вижу:

240 звонков в 240.005s - 0.99979 звонков в секунду

В самом деле, я видел, как дрифт поднимается до .007s, а затем вниз до .003s. Я думаю, что ваша техника измерения ошибочна.

В Firefox я вижу, что он дрейфует еще сильнее (+/- 8ms в любом направлении), а затем компенсируется в следующем прогоне. Большую часть времени я вижу "1.000000 вызовов/секунду" в Firefox.

Ответ 5

Алгоритм компенсации временных различий не работает в моем случае использования. Я пытаюсь обойти проблему синхронизации, которая возникает при запуске setTimeout, а затем изменить активную вкладку/окно в браузере Edge. Временной цикл переходит в замедленное движение в неактивном окне. И это не помогает пробовать меньший тайм-аут... кажется, что он входит в какой-то режим зомби. Итак, setTimeout (func, 3000) примерно в то же время, что и setTimeout (func, 3). Кто-нибудь знает об этом?