Как браузеры приостанавливают / изменяют Javascript, когда вкладка или окно не активны?

168

Фон: я делаю некоторые тесты пользовательского интерфейса, которые должны определить, обращают ли люди внимание или нет. Но этот вопрос не об API видимости страницы .

В частности, я хотел бы знать, как повлияет мой код Javascript, если текущая вкладка не активна или окно браузера не активно в разных браузерах. До сих пор я выкопал следующее:

У меня есть следующие вопросы:

  • Кроме мобильных браузеров, настольные браузеры когда-нибудь приостанавливают выполнение JS, когда вкладка не активна? Когда и какие браузеры?
  • Какие браузеры уменьшают setIntervalповтор? Это просто уменьшено до предела или в процентах? Например, если у меня будет повторение 10 мс против повторения 5000 мс, как это повлияет на каждый?
  • Происходят ли эти изменения, если окно не в фокусе, а не только вкладка? (Я полагаю, это будет сложнее обнаружить, так как для этого требуется ОС API.)
  • Существуют ли какие-либо другие эффекты, которые не наблюдались бы в активной вкладке? Могут ли они испортить вещи, которые в противном случае выполнялись бы правильно (например, вышеупомянутые тесты Жасмин)?
Эндрю Мао
источник
Если они приостановлены, такие сайты, как Facebook, не будут получать сообщения чата на фоновых вкладках.
Иосиф
1
Да, здесь нет пауз, но я помню, что читал, что setInterval/ setTimeoutвремена под 1000 мс меняются на 1000 мс, когда вкладка / окно размыты
Ян
19
@ProfPickle Веб-мастера? В самом деле? Это вопрос программирования JS.
Эндрю Мао
1
@lan setInterval/ setTimeouttimes до 1000 мс изменяются на 1000 мс, когда вкладка / окно размыты. Непонятно, что вы пытались передать
Amol M Kulkarni
4
+1 Отличный вопрос. Было бы хорошо увидеть параллельное сравнение поведения браузера, так как я считаю, что ограничивающее поведение, когда вкладки не активны, не является частью какого-либо стандарта.
UpTheCreek

Ответы:

190

Тест Один

Я написал тест специально для этой цели:
Распределение частоты кадров: setInterval vs requestAnimationFrame

Примечание. Этот тест требует значительных ресурсов процессора. requestAnimationFrameне поддерживается IE 9- и Opera 12-.

Тест регистрирует фактическое время, необходимое для выполнения setIntervalи requestAnimationFrameв разных браузерах, и выдает результаты в виде дистрибутива. Вы можете изменить количество миллисекунд, setIntervalчтобы увидеть, как он работает при разных настройках. setTimeoutработает аналогично a в setIntervalотношении задержек. requestAnimationFrameкак правило, по умолчанию 60fps в зависимости от браузера. Чтобы увидеть, что происходит, когда вы переключаетесь на другую вкладку или у вас неактивное окно, просто откройте страницу, переключитесь на другую вкладку и подождите некоторое время. Он продолжит регистрировать фактическое время, необходимое для этих функций, на неактивной вкладке.

Тест Два

Другой способ проверить это состоит в том, чтобы периодически регистрировать метку времени setIntervalи requestAnimationFrameпросматривать ее на отдельной консоли. Вы можете видеть, как часто оно обновляется (или обновляется) когда вы делаете вкладку или окно неактивными.

Полученные результаты

Chrome
Chrome ограничивает минимальный интервал setIntervalдо 1000 мс, когда вкладка неактивна. Если интервал превышает 1000 мс, он будет работать с указанным интервалом. Неважно, если окно не в фокусе, интервал ограничен только при переключении на другую вкладку. requestAnimationFrameПауза, когда вкладка неактивна.

// Provides control over the minimum timer interval for background tabs.
const double kBackgroundTabTimerInterval = 1.0;

https://codereview.chromium.org/6546021/patch/1001/2001

Firefox
Аналогично Chrome, Firefox ограничивает минимальный интервал setIntervalдо 1000 мс, когда вкладка (не окно) неактивна. Однако, requestAnimationFrameкогда вкладка неактивна , она работает экспоненциально медленнее, каждый кадр занимает 1 с, 2 с, 4 с, 8 с и т. Д.

// The default shortest interval/timeout we permit
#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms

https://hg.mozilla.org/releases/mozilla-release/file/0bf1cadfb004/dom/base/nsGlobalWindow.cpp#l296

Internet Explorer
IE не ограничивает задержку, setIntervalкогда вкладка неактивна, но делает паузу requestAnimationFrameв неактивных вкладках. Неважно, не в фокусе ли окно.

Edge
Начиная с Edge 14, setIntervalограничивается 1000 мс в неактивных вкладках. requestAnimationFrameвсегда приостанавливается в неактивных вкладках.

Safari
Как и Chrome, Safari ограничивается setInterval1000 мс, когда вкладка неактивна. requestAnimationFrameтакже приостановлен

Opera
С момента принятия движка Webkit Opera демонстрирует то же поведение, что и Chrome. setIntervalограничивается 1000 мс и requestAnimationFrameприостанавливается, когда вкладка неактивна.

Резюме

Повторяющиеся интервалы для неактивных вкладок:

           setInterval      requestAnimationFrame 
Chrome
9 - не влияет, не поддерживается
10 без изменений приостановлено
11+> = 1000 мс приостановлено

Fire Fox
3- не влияет не поддерживается
4 не влияет 1с
5+> = 1000 мс 2 н S (N = число кадров с момента бездействия)

IE
9 - не влияет, не поддерживается
10+ не влияет на паузу

край
13- не влияет на паузу
14+> = 1000 мс приостановлено

Сафари
5- не влияет не поддерживается
6 не влияет на паузу
7+> = 1000 мс приостановлено

опера
12 - не влияет, не поддерживается
15+> = 1000 мс приостановлено
Antony
источник
Отличный ответ. Любые другие возможные известные различия для функций, кроме setIntervalи requestAnimationFrame?
Андрей Мао
1
@ AndrewMao Не то, чтобы я знал. Я сталкивался с этой проблемой, когда работал над библиотекой, чтобы надежно определить, включен ли JS с помощью setIntervalи requestAnimationFrame. То, о чем я знаю, это то , что они ведут setTimeoutсебя аналогично setInterval, поскольку у них обоих одинаковый минимальный фоновый интервал в Firefox и Chrome, а в других браузерах нет видимых ограничений.
Антоний
2
Минимальное значение в Firefox setInterval, по-видимому, можно изменить, открыв URL-адрес about:configв браузере и изменив dom.min_background_timeout_valueзначение на значение, отличное от 1000.
Jonas Berlin,
Могу ли я использовать это для перезагрузки страницы каждые 5 секунд, когда браузер свернут? Вот мой вопрос.
Шайют
1
Обратите внимание, что Chrome не приостанавливает / не уменьшает скорость requestAnimationFrameвызова, если пользователь просто переключает приложение (Alt + Tab из Chrome). Пока вкладка активна в Chrome, «частота кадров» более или менее постоянна.
Марк
11

Что я заметил: на неактивных вкладках в Chrome все ваши setTimeout(должны быть одинаковыми setInterval) ожидания менее 1000 мс округляются до 1000 мс . Я думаю, что более длительные тайм-ауты не изменены.

Похоже, что поведение с Chrome 11 и Firefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

Кроме того, я не думаю, что это ведет себя так, когда все окно неактивно (но это довольно легко исследовать).


источник
1
Кажется, что jQuery focusи blurсобытия обнаруживают как переключатели вкладок, так и окон, поэтому он может работать в обоих направлениях. Но мне интересно, как окно определяет, действительно ли оно видно или нет.
Андрей Мао
2
На самом деле он не имеет никакого отношения к jQuery или Javascript, поскольку является внутренней реализацией браузера.
Можете ли вы подтвердить это сейчас в конце 2016 года?
Vsync
0

Более новый ответ, дополняющий их: в chrome 78.0.3904.108 я замечаю, что все эти таймауты (не только те, что ниже 1000 мс) занимают немного больше времени, чем ожидалось, когда я перехожу на другую вкладку, а затем возвращаюсь. Поведение, которое я вижу, более правильно описывается как «Все тайм-ауты на неактивных вкладках могут быть задержаны на некоторую дополнительную сумму, максимум до 1000 мс». :

let timeouts = [ 500, 1000, 2000, 3000, 10000 ];

let minExcess = document.getElementsByClassName('minExcess')[0];

timeouts.forEach(ms => {
  let elem = document.getElementsByClassName(`t${ms}`)[0];
  let cnt = 0;
  
  let lastMs = +new Date();
  let f = () => {
    let curMs = +new Date();
    let disp = document.createElement('p');
    let net = curMs - lastMs;
    lastMs = curMs;
        
    setTimeout(f, ms);
    if (minExcess.value && (net - ms) < parseInt(minExcess.value)) return;
    
    disp.innerText = `${net},`;
    elem.appendChild(disp);
    if (++cnt > 10) elem.firstElementChild.remove();
    
  };
  setTimeout(f, ms);
  
});
body { font-size: 80%; }
div {
  max-height: 80px;
  overflow-x: auto;
  background-color: rgba(0, 0, 0, 0.1);
  margin-bottom: 2px;
  white-space: nowrap;
}
p { margin: 0; }
div > p {
  margin: 0;
  display: inline-block;
  vertical-align: top;
  margin-right: 2px;
}
input { margin: 0 0 10px 0; }
.t500:before { display: block; content: '500ms'; font-weight: bold; }
.t1000:before { display: block; content: '1000ms'; font-weight: bold; }
.t2000:before { display: block; content: '2000ms'; font-weight: bold; }
.t3000:before { display: block; content: '3000ms'; font-weight: bold; }
.t10000:before { display: block; content: '10000ms'; font-weight: bold; }
<p>Ignore any values delayed by less than this amount:</p>
<input type="text" class="minExcess" value="200" pattern="^[0-9]*$"/>
<div class="timeout t500"></div>
<div class="timeout t1000"></div>
<div class="timeout t2000"></div>
<div class="timeout t3000"></div>
<div class="timeout t10000"></div>

Гирсам
источник