¿Cómo pausan / cambian los navegadores Javascript cuando la pestaña o ventana no está activa?

168

Antecedentes: estoy haciendo algunas pruebas de interfaz de usuario que necesitan detectar si las personas están prestando atención o no. Pero, esta pregunta no se trata de la API de visibilidad de la página .

Específicamente, me gustaría saber cómo se verá afectado mi código Javascript si la pestaña actual no está activa, o la ventana del navegador no está activa, en diferentes navegadores. He desenterrado lo siguiente hasta ahora:

Tengo las siguientes preguntas:

  • Además de los navegadores móviles, ¿los navegadores de escritorio pausan la ejecución de JS cuando una pestaña no está activa? ¿Cuándo y qué navegadores?
  • ¿Qué navegadores reducen la setIntervalrepetición? ¿Se reduce a un límite o en un porcentaje? Por ejemplo, si tengo una repetición de 10 ms frente a una repetición de 5000 ms, ¿cómo se verá afectado cada uno?
  • ¿Se producen estos cambios si la ventana está desenfocada, en lugar de solo la pestaña? (Me imagino que sería más difícil de detectar, ya que requiere la API del sistema operativo).
  • ¿Hay otros efectos que no se observarían en una pestaña activa? ¿Podrían estropear cosas que de otro modo se ejecutarían correctamente (es decir, las pruebas de Jasmine mencionadas anteriormente)?
Andrew Mao
fuente
Si están pausados, los sitios como Facebook no recibirían ningún mensaje de chat en las pestañas de fondo.
Joseph
1
Sí, no hay pausa, pero sí recuerdo haber leído que las setInterval/ setTimeoutveces por debajo de 1000 ms se cambian a 1000 ms cuando la pestaña / ventana está borrosa
Ian
19
@ProfPickle Webmasters? De Verdad? Esta es una pregunta de programación JS.
Andrew Mao
1
@lan setInterval/ setTimeouttimes under 1000ms se cambian a 1000ms cuando la pestaña / ventana está borrosa. No está claro lo que ha intentado transmitir
Amol Kulkarni M
44
+1 Gran pregunta. Sería bueno ver una comparación lado a lado de los comportamientos del navegador, ya que creo que el comportamiento de sujeción cuando las pestañas no están activas no forma parte de ningún estándar.
UpTheCreek

Respuestas:

190

Prueba uno

He escrito una prueba específicamente para este propósito:
Distribución de velocidad de cuadros: setInterval vs requestAnimationFrame

Nota: Esta prueba requiere bastante CPU. requestAnimationFrameno es compatible con IE 9 y Opera 12.

La prueba registra el tiempo real que se necesita para una setIntervaly requestAnimationFramepara funcionar en distintos navegadores, y le da los resultados en forma de una distribución. Puede cambiar el número de milisegundos para setIntervalver cómo se ejecuta en diferentes configuraciones. setTimeoutfunciona de manera similar a a setIntervalcon respecto a los retrasos. requestAnimationFramegeneralmente está predeterminado a 60 fps según el navegador. Para ver qué sucede cuando cambia a una pestaña diferente o tiene una ventana inactiva, simplemente abra la página, cambie a una pestaña diferente y espere un momento. Continuará registrando el tiempo real que lleva estas funciones en una pestaña inactiva.

Prueba dos

Otra forma de probarlo es registrar la marca de tiempo repetidamente con setIntervaly requestAnimationFramey verlo en una consola separada. Puede ver con qué frecuencia se actualiza (o si alguna vez se actualiza) cuando desactiva la pestaña o ventana.

Resultados

Chrome
Chrome limita el intervalo mínimo de setIntervalalrededor de 1000 ms cuando la pestaña está inactiva. Si el intervalo es superior a 1000 ms, se ejecutará en el intervalo especificado. No importa si la ventana está desenfocada, el intervalo se limita solo cuando cambia a una pestaña diferente. requestAnimationFrameestá en pausa cuando la pestaña está inactiva.

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

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

Firefox
Similar a Chrome, Firefox limita el intervalo mínimo de setIntervalalrededor de 1000 ms cuando la pestaña (no la ventana) está inactiva. Sin embargo, se requestAnimationFrameejecuta exponencialmente más lento cuando la pestaña está inactiva, con cada cuadro tomando 1s, 2s, 4s, 8s y así sucesivamente.

// 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 no limita el retraso setIntervalcuando la pestaña está inactiva, pero se detiene requestAnimationFrameen pestañas inactivas. No importa si la ventana está desenfocada o no.

Edge
Comenzando desde Edge 14, setIntervaltiene un límite de 1000 ms en pestañas inactivas. requestAnimationFramesiempre está en pausa en pestañas inactivas.

Safari
Al igual que Chrome, Safari se setIntervallimita a 1000 ms cuando la pestaña está inactiva. requestAnimationFrameestá en pausa también.

Opera
Desde la adopción del motor Webkit, Opera exhibe el mismo comportamiento que Chrome. setIntervaltiene un límite de 1000 ms y requestAnimationFramese detiene cuando la pestaña está inactiva.

Resumen

Repetir intervalos para pestañas inactivas:

           setInterval      requestAnimationFrame 
Chrome
9- no afectado no soportado
10 no afectados en pausa
11+> = 1000 ms en pausa

Firefox
3- no afectado no soportado
4 no afectados 1s
5+> = 1000ms 2 n s (n = número de cuadros desde inactividad)

ES DECIR
9- no afectado no soportado
10+ no afectados en pausa

Borde
13- no afectado en pausa
14+> = 1000 ms en pausa

Safari
5- no afectado no soportado
6 no afectados en pausa
7+> = 1000 ms en pausa

Ópera
12- no afectado no soportado
15+> = 1000 ms en pausa
Antonio
fuente
Gran respuesta. ¿Alguna otra diferencia posible conocida para funciones distintas de setIntervaly requestAnimationFrame?
Andrew Mao
1
@AndrewMao No que yo sepa. Me encontré con este problema cuando estaba trabajando en una biblioteca para detectar de manera confiable si JS se volvió a habilitar con setIntervaly requestAnimationFrame. Lo que sé es que se setTimeoutcomporta de manera similar setInterval, ya que ambos tienen el mismo intervalo de fondo mínimo en Firefox y Chrome, y no tienen un límite aparente en otros navegadores.
Antony
2
El valor mínimo de Firefox setInterval aparentemente puede cambiarse abriendo la url about:configen el navegador y cambiando el dom.min_background_timeout_valuevalor a algo más que 1000.
Jonas Berlin
¿Puedo usar esto para recarga de la página cada 5 segundos cuando se minimiza navegador?, aquí es mi pregunta.
shaijut
1
Tenga en cuenta que Chrome no pausa / reduce la velocidad a la que requestAnimationFramese llama si el usuario simplemente cambia la aplicación (Alt + Tab fuera de Chrome). Mientras la pestaña esté activa en Chrome, la "velocidad de fotogramas" es más o menos constante.
Marc
11

Lo que observé: en las pestañas inactivas en Chrome , todas sus esperas setTimeout(deben ser las mismas para setInterval) menos de 1000 ms se redondean a 1000 ms . Creo que los tiempos de espera más largos no se modifican.

Parece ser el comportamiento desde Chrome 11 y Firefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

Además, no creo que se comporte de esta manera cuando toda la ventana está inactiva (pero parece bastante fácil de investigar).


fuente
1
jQuery focusy los blureventos parecen detectar tanto los cambios de pestaña como de ventana, por lo que podría funcionar en ambos sentidos. Pero me pregunto cómo detecta la ventana si es realmente visible o no.
Andrew Mao
2
En realidad no tiene conexión con jQuery o Javascript, ya que es una implementación interna del navegador.
¿Puedes confirmar esto ahora a finales de 2016?
vsync
0

Una respuesta más nueva para complementar estos: en Chrome 78.0.3904.108 Noto que todos estos tiempos de espera (no solo aquellos por debajo de 1000 ms) tardan un poco más de lo esperado cuando me muevo a una pestaña diferente, y luego vuelvo. El comportamiento que estoy viendo se describe más correctamente como "Todos los tiempos de espera en pestañas inactivas pueden retrasarse en una cantidad adicional, hasta un máximo de 1000 ms". :

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>

Gershom
fuente