In che modo i browser sospendono / cambiano Javascript quando la scheda o la finestra non sono attive?


168

Sfondo: sto facendo alcuni test dell'interfaccia utente che devono rilevare se le persone prestano attenzione o meno. Ma questa domanda non riguarda l'API di visibilità della pagina .

In particolare, vorrei sapere come sarà influenzato il mio codice Javascript se la scheda corrente non è attiva, o la finestra del browser non è attiva, in diversi browser. Finora ho scoperto quanto segue:

Ho le seguenti domande:

  • Oltre ai browser mobili, i browser desktop mettono mai in pausa l'esecuzione di JS quando una scheda non è attiva? Quando e quali browser?
  • Quali browser ridurre la setIntervalripetizione? È solo ridotto a un limite o in percentuale? Ad esempio, se ho una ripetizione di 10 ms rispetto a una ripetizione di 5000ms, in che modo saranno interessati?
  • Queste modifiche si verificano se la finestra non è a fuoco, anziché solo la scheda? (Immagino che sarebbe più difficile da rilevare, in quanto richiede l'API del sistema operativo.)
  • Ci sono altri effetti che non verrebbero osservati in una scheda attiva? Potrebbero rovinare cose che altrimenti verrebbero eseguite correttamente (cioè i test di Jasmine sopra menzionati)?

Se sono in pausa, siti come Facebook non riceveranno alcun messaggio di chat nelle schede in background.
Joseph

1
Sì, non c'è pausa, ma ricordo di aver letto che i setInterval/ setTimeoutvolte sotto i 1000 ms vengono cambiati in 1000 ms quando la scheda / finestra è sfocata
Ian

19
@ProfPickle Webmasters? Veramente? Questa è una domanda di programmazione JS.
Andrew Mao,

1
@lan setInterval/ i setTimeouttempi inferiori a 1000ms vengono cambiati in 1000ms quando la scheda / finestra è sfocata. Non è chiaro cosa hai cercato di comunicare
Amol M Kulkarni

4
+1 Ottima domanda. Sarebbe bello vedere un confronto fianco a fianco dei comportamenti del browser, poiché credo che il comportamento di blocco quando le schede non sono attive non fa parte di alcuno standard.
UpTheCreek

Risposte:


190

Test One

Ho scritto un test appositamente per questo scopo:
Distribuzione della frequenza dei fotogrammi: setInterval vs requestAnimationFrame

Nota: questo test richiede molta CPU. requestAnimationFramenon è supportato da IE 9- e Opera 12-.

Il test registra il tempo effettivo impiegato da un setIntervale requestAnimationFrameper l'esecuzione in diversi browser, e ti dà i risultati sotto forma di una distribuzione. È possibile modificare il numero di millisecondi per setIntervalvedere come funziona con impostazioni diverse. setTimeoutfunziona in modo simile a setIntervalrispetto ai ritardi. requestAnimationFramein genere il valore predefinito è 60fps a seconda del browser. Per vedere cosa succede quando passi a un'altra scheda o hai una finestra inattiva, apri semplicemente la pagina, passa a un'altra scheda e attendi qualche istante. Continuerà a registrare il tempo effettivo impiegato per queste funzioni in una scheda inattiva.

Prova due

Un altro modo per testarlo è quello di accedere al timestamp ripetutamente con setIntervaled requestAnimationFramee visualizzarlo in una console indipendente. Puoi vedere con che frequenza viene aggiornato (o se mai aggiornato) quando rendi inattiva la scheda o la finestra.

risultati

Chrome
Chrome limita l'intervallo minimo di setIntervalcirca 1000 ms quando la scheda è inattiva. Se l'intervallo è superiore a 1000 ms, verrà eseguito all'intervallo specificato. Non importa se la finestra è sfocata, l'intervallo è limitato solo quando si passa a una scheda diversa. requestAnimationFrameviene messo in pausa quando la scheda è inattiva.

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

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

Firefox
Simile a Chrome, Firefox limita l'intervallo minimo di setIntervalcirca 1000 ms quando la scheda (non la finestra) è inattiva. Tuttavia, requestAnimationFrameviene eseguito in modo esponenziale più lento quando la scheda è inattiva, con ciascun fotogramma che richiede 1s, 2s, 4s, 8s e così via.

// 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 non limita il ritardo setIntervalquando la scheda è inattiva, ma si interrompe requestAnimationFramenelle schede inattive. Non importa se la finestra è sfocata o meno.

Bordo
A partire da Bordo 14, setIntervalè limitato a 1000 ms in schede inattive. requestAnimationFrameviene sempre messo in pausa nelle schede inattive.

Safari
Proprio come Chrome, Safari si setIntervalblocca a 1000 ms quando la scheda è inattiva. requestAnimationFrameè anche in pausa.

Opera
Dall'adozione del motore Webkit, Opera presenta lo stesso comportamento di Chrome. setIntervalè limitato a 1000 ms e requestAnimationFrameviene messo in pausa quando la scheda è inattiva.

Sommario

Ripetizione degli intervalli per le schede inattive:

           setInterval      requestAnimationFrame 
Chrome
9- non interessato non supportato
10 non interessato in pausa
11+> = 1000ms in pausa

Firefox
3- non interessato non supportato
4 non interessati 1s
5+> = 1000ms 2 n s (n = numero di frame dall'inattività)

IE
9- non interessato non supportato
10+ non interessati in pausa

Bordo
13- non influenzato in pausa
14+> = 1000ms in pausa

Safari
5- non interessato non supportato
6 non interessato in pausa
7+> = 1000ms in pausa

musica lirica
12- non interessato non supportato
15+> = 1000ms in pausa

Bella risposta. Altre possibili differenze note per funzioni diverse da setIntervale requestAnimationFrame?
Andrew Mao,

1
@AndrewMao Non di cui sono a conoscenza. Mi sono imbattuto in questo problema mentre stavo lavorando su una libreria per rilevare in modo affidabile se JS è stato riattivato con setIntervale requestAnimationFrame. La cosa che so è che setTimeoutsi comporta in modo simile setInterval, in quanto entrambi hanno lo stesso intervallo di background minimo in Firefox e Chrome e nessun limite apparente in altri browser.
Antony,

2
Apparentemente il valore minimo di Firefox setInterval può essere modificato aprendo l'URL about:confignel browser e cambiando il dom.min_background_timeout_valuevalore in qualcosa di diverso da 1000.
Jonas Berlin

posso usarlo per ricaricare la pagina ogni 5 secondi quando il browser è ridotto a icona? Ecco la mia domanda.
shaijut,

1
Nota che Chrome non mette in pausa / riduce la velocità con cui requestAnimationFrameviene chiamato se l'utente cambia semplicemente applicazione (Alt + Tab su Chrome). Finché la scheda è attiva in Chrome, la "frequenza dei fotogrammi" è più o meno costante.
Marc,

11

Quello che ho osservato: su schede inattive in Chrome , tutte le tue setTimeout(devono essere le stesse per setInterval) aspettare meno di 1000ms sono arrotondate a 1000ms . Penso che i timeout più lunghi non vengano modificati.

Sembra essere il comportamento dopo Chrome 11 e Firefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

Inoltre, non penso che si comporti in questo modo quando l'intera finestra è inattiva (ma sembra abbastanza facile da investigare).


1
Sembra che jQuery focuse gli blureventi rilevino entrambi gli interruttori di tabulazione e finestra, quindi potrebbe funzionare in entrambi i modi. Ma mi chiedo come la finestra rilevi se è effettivamente visibile o meno.
Andrew Mao,

2
In realtà non ha alcuna connessione con jQuery o Javascript poiché è un'implementazione interna del browser.

Puoi confermarlo ora alla fine del 2016?
vsync,

0

Una risposta più recente a complemento di questi: su Chrome 78.0.3904.108 noto che tutti questi timeout (non solo quelli inferiori a 1000 ms) impiegano un po 'più del previsto quando passo a una scheda diversa e poi torno. Il comportamento che sto vedendo è descritto più correttamente come "Tutti i timeout nelle schede inattive possono essere ritardati di un importo aggiuntivo, fino a un massimo di 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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.