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


130

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

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

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


Интересный! Можете ли вы определить, приостанавливает ли 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: используйте веб-воркеров .


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


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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.