Chrome: таймауты / интервал приостановлены на фоновых вкладках?

130

Я проверял точность setTimeoutиспользования этого теста . Теперь я заметил, что (как и ожидалось) setTimeoutне очень точный, но для большинства приборов не очень точный. Теперь, если я запускаю тест в Chrome и позволяю ему работать в фоновой вкладке (так, переключаясь на другую вкладку и просматривая ее), возвращаюсь к тесту и проверяю результаты (если тест завершен), они кардинально изменяются. Похоже, что таймауты стали намного медленнее. При тестировании в FF4 или IE9 этого не произошло.

Таким образом, похоже, что Chrome приостанавливает или, по крайней мере, замедляет выполнение javascript на вкладке, у которой нет фокуса. Не удалось найти в сети много информации по этой теме. Это означало бы, что мы не можем запускать фоновые задачи, такие как, например, периодическая проверка на сервере с помощью вызовов XHR и setInterval(я подозреваю, что вижу такое же поведение setInterval, напишет тест, если время со мной).

Кто-нибудь сталкивался с этим? Есть ли способ обхода этой приостановки / замедления? Вы бы назвали это ошибкой, и я должен зарегистрировать это как таковую?

KooiInc
источник
Интересный! Можете ли вы определить, приостанавливает ли Chrome и возобновляет таймер или перезапускает его, после повторного доступа к вкладке? Или поведение случайное? Может ли это иметь какое-либо отношение к тому, что Chrome запускает вкладки в независимых процессах?
HyderA
@gAMBOOKa: посмотрите ответ @pimvdb. Скорее всего, это замедление максимум до одного раза в секунду.
KooiInc
Спустя 4 года эта проблема все еще существует. У меня есть setTimeOut для div с a transition, поэтому не все div переходят одновременно, а на самом деле через 15 мс после каждого, создавая некоторый эффект качения. Когда я перехожу на другую вкладку и возвращаюсь через некоторое время, все блоки div переходят одновременно, и setTimeOutполностью игнорируется. Это не большая проблема для моего проекта, но странное и нежелательное дополнение.
Rvervuurt
Для нашей анимации, которая вызывала setTimeout в последовательности, решение для нас было просто убедиться, что мы помним дескриптор / идентификатор таймера (он возвращается из setTimeout), и перед установкой нового таймера мы сначала вызываем clearTimeout, если мы получил ручку. В нашем случае это означает, что, когда вы вернетесь на вкладку, может быть некоторая начальная странность с точки зрения воспроизводимой анимации, но она довольно быстро устраняется, и обычная анимация возобновляется. Сначала мы думали, что это проблема с нашим кодом.
Action Dan

Ответы:

89

Я недавно спросил об этом, и это поведение по замыслу. Когда вкладка неактивна, функция вызывается не чаще одного раза в секунду. Вот изменение кода .

Возможно, это поможет: как сделать так, чтобы setInterval также работал, когда вкладка в Chrome неактивна?

TL; DR: используйте веб-воркеров .

pimvdb
источник
3
спасибо, я должен был посмотреть "неактивную вкладку". То, что вы не являетесь носителем английского языка, иногда является препятствием.
KooiInc
1
@Kooilnc: Нет проблем :) Я тоже не говорю по-английски.
pimvdb
22

Есть решение использовать Web Workers, потому что они выполняются в отдельном процессе и не замедляются.

Я написал крошечный скрипт, который можно использовать без изменения вашего кода - он просто переопределяет функции setTimeout, clearTimeout, setInterval, clearInterval

Просто включите его перед всем своим кодом

http://github.com/turuslan/HackTimer

Руслан Тушов
источник
7
Это хорошо, но имейте в виду, что: 1. Рабочие не имеют доступа к DOM, 2. Рабочие выполняются только тогда, когда они находятся в файле сами по себе. Это не замена setTimeout во многих случаях.
Призрак Мадары
1
Вы правы, но некоторые современные браузеры позволяют использовать воркеров без собственных файлов с помощью Blobs ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
Руслан Тушов
1
Даже при этом веб-воркерам не хватает многих функций (а именно, DOM), которые позволяют им быть безопасной заменой setTimeout и других.
Призрак Мадары
Как насчет кода, который должен выполняться во внешнем интерфейсе, например, тяжелых задач обработки графики, которые мы хотели бы завершить, пока мы делаем другие вещи?
Майкл
Что ж, вы можете создавать воркеров, сервис-воркеров и использовать API холста, используя URL-адрес данных. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'), Это также хороший способ проверить, есть ли у вас поддержка выражений импорта:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
Фабио Сантос,
9

Воспроизведение пустого звука заставляет браузер сохранять производительность - я обнаружил это после прочтения этого комментария: Как заставить JavaScript работать с нормальной скоростью в Chrome, даже когда вкладка не активна?

Мне нужна неограниченная производительность по запросу для браузерной игры, в которой используются WebSockets, поэтому по опыту я знаю, что использование WebSockets не обеспечивает неограниченной производительности, но, судя по тестам, воспроизведение аудиофайла, похоже, гарантирует это.

Вот 2 пустых звуковых цикла, которые я создал для этой цели, вы можете свободно использовать их в коммерческих целях: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Они включают шум -58 дБ, -60 дБ не работает)

Я играю в них по запросу пользователя с помощью Howler.js: https://github.com/goldfire/howler.js

function performance_trick()
{
    if(sounds.empty) return sounds.empty.play();
    sounds.empty = new Howl({
        src: ['/sounds/loops/empty_loop_for_js_performance.ogg','/sounds/loops/empty_loop_for_js_performance.wav'],
        volume:0.5,
        autoplay: true, loop: true,
    });
}

Печально, что нет встроенного метода для включения / выключения полной производительности javascript по умолчанию, тем не менее, крипто-майнеры могут захватить все ваши вычислительные потоки с помощью Web Workers без какого-либо запроса: |

Каан Сорал
источник
Спасибо, 58 дБ очень хорошо слышно в наушниках, но отключение звука на сайте решает эту проблему
Каан Сорал
1

Я выпустил пакет npm с рабочим интервалом , который реализует setInterval и clearInterval с использованием Web-Workers, чтобы поддерживать работу на неактивных вкладках для Chrome, Firefox и IE.

В большинстве современных браузеров (Chrome, Firefox и IE) интервалы (таймеры окон) ограничены, чтобы срабатывать не чаще одного раза в секунду на неактивных вкладках.

Вы можете найти больше информации на

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Timeouts_and_intervals

gorkemcnr
источник
0

Я обновил ядро ​​jQuery до версии 1.9.1, и это устранило несоответствие интервалов в неактивных вкладках. Сначала я попробую это, а затем изучу другие варианты переопределения кода.

Кэри Эстес
источник
с какой версии вы обновились? У меня возникли проблемы с таймаутом (слайдеры галереи) с версией ~ 1.6
dmi3y 08
0

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

<script>

var nowMillisTimeout = [];
var timeout = [];
var nowMillisInterval = [];
var interval = [];

function getCurrentMillis(){
    var d = new Date();
    var now = d.getHours()+""+d.getMinutes()+""+d.getSeconds()+""+d.getMilliseconds();
    return now;
}

function setAccurateTimeout(callbackfunction, millis, id=0){
    nowMillisTimeout[id] = getCurrentMillis();
    timeout[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisTimeout[id] + +millis)){callbackfunction.call(); clearInterval(timeout[id]);} }, 10);
}

function setAccurateInterval(callbackfunction, millis, id=0){
    nowMillisInterval[id] = getCurrentMillis();
    interval[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisInterval[id] + +millis)){callbackfunction.call(); nowMillisInterval[id] = getCurrentMillis();} }, 10);
}

//usage
setAccurateTimeout(function(){ console.log('test timeout'); }, 1000, 1);

setAccurateInterval(function(){ console.log('test interval'); }, 1000, 1);

</script>
SwiftNinjaPro
источник