Comment les navigateurs mettent-ils en pause / modifient Javascript lorsque l'onglet ou la fenêtre n'est pas actif?

168

Contexte: je fais des tests d'interface utilisateur qui doivent détecter si les gens font attention ou non. Mais cette question ne concerne pas l'API de visibilité de page .

Plus précisément, j'aimerais savoir comment mon code Javascript sera affecté si l'onglet actuel n'est pas actif, ou si la fenêtre du navigateur n'est pas active, dans différents navigateurs. J'ai découvert ce qui suit jusqu'à présent:

J'ai les questions suivantes:

  • À part les navigateurs mobiles, les navigateurs de bureau mettent-ils en pause l'exécution de JS lorsqu'un onglet n'est pas actif? Quand et quels navigateurs?
  • Quels navigateurs réduisent la setIntervalrépétition? Est-il simplement réduit à une limite ou à un pourcentage? Par exemple, si j'ai une répétition de 10 ms par rapport à une répétition de 5000 ms, comment chacun sera-t-il affecté?
  • Ces changements se produisent-ils si la fenêtre est floue, par opposition à l'onglet uniquement? (J'imagine que ce serait plus difficile à détecter, car cela nécessite l'API du système d'exploitation.)
  • Y a-t-il d'autres effets qui ne seraient pas observés dans un onglet actif? Pourraient-ils gâcher des choses qui autrement s'exécuteraient correctement (c'est-à-dire les tests Jasmine susmentionnés)?
Andrew Mao
la source
S'ils sont mis en pause, des sites comme Facebook ne recevront aucun message de discussion sur les onglets d'arrière-plan.
Joseph
1
Oui, il n'y a pas de pause, mais je me souviens avoir lu que les setInterval/ setTimeouttimes sous 1000ms sont changés en 1000ms lorsque l'onglet / la fenêtre est floue
Ian
19
@ProfPickle Webmasters? Vraiment? C'est une question de programmation JS.
Andrew Mao
1
@lan setInterval/ setTimeoutfois sous 1000ms sont changés en 1000ms lorsque l'onglet / la fenêtre est floue. Pas clair ce que vous avez essayé de transmettre
Amol M Kulkarni
4
+1 Excellente question. Il serait bon de voir une comparaison côte à côte des comportements du navigateur, car je pense que le comportement de serrage lorsque les onglets ne sont pas actifs ne fait partie d'aucune norme.
UpTheCreek

Réponses:

190

Test un

J'ai écrit un test spécialement à cet effet:
Distribution de la fréquence d'images: setInterval vs requestAnimationFrame

Remarque: ce test est assez gourmand en ressources processeur. requestAnimationFramen'est pas pris en charge par IE 9- et Opera 12-.

Le test enregistre le temps réel nécessaire à un setIntervalet requestAnimationFramepour s'exécuter dans différents navigateurs et vous donne les résultats sous la forme d'une distribution. Vous pouvez modifier le nombre de millisecondes pour setIntervalvoir comment il s'exécute sous différents paramètres. setTimeoutfonctionne de la même manière que setIntervalpour les retards. requestAnimationFramegénéralement par défaut au 60fps selon le navigateur. Pour voir ce qui se passe lorsque vous passez à un autre onglet ou que vous avez une fenêtre inactive, ouvrez simplement la page, passez à un autre onglet et attendez un moment. Il continuera à enregistrer le temps réel nécessaire pour ces fonctions dans un onglet inactif.

Test deux

Une autre façon de le tester consiste à enregistrer l'horodatage à plusieurs reprises avec setIntervalet requestAnimationFrameet à l'afficher dans une console détachée. Vous pouvez voir à quelle fréquence il est mis à jour (ou s'il a déjà été mis à jour) lorsque vous désactivez l'onglet ou la fenêtre.

Résultats

Chrome
Chrome limite l'intervalle minimum setIntervalà environ 1000 ms lorsque l'onglet est inactif. Si l'intervalle est supérieur à 1000 ms, il s'exécutera à l'intervalle spécifié. Peu importe si la fenêtre est floue, l'intervalle est limité uniquement lorsque vous passez à un autre onglet. requestAnimationFrameest suspendu lorsque l'onglet est inactif.

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

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

Firefox À l'
instar de Chrome, Firefox limite l'intervalle minimum d' setIntervalenviron 1000 ms lorsque l'onglet (et non la fenêtre) est inactif. Cependant, requestAnimationFrames'exécute de manière exponentielle plus lente lorsque l'onglet est inactif, chaque image prenant 1s, 2s, 4s, 8s, etc.

// 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 ne limite pas le délai d' setIntervalinactivité de l'onglet, mais il s'interrompt requestAnimationFramedans les onglets inactifs. Peu importe que la fenêtre soit floue ou non.

Edge
À partir de Edge 14, setIntervalest plafonné à 1000 ms dans les onglets inactifs. requestAnimationFrameest toujours en pause dans les onglets inactifs.

Safari
Tout comme Chrome, Safari plafonne setIntervalà 1000 ms lorsque l'onglet est inactif. requestAnimationFrameest également mis en pause.

Opera
Depuis l'adoption du moteur Webkit, Opera présente le même comportement que Chrome. setIntervalest plafonné à 1000 ms et requestAnimationFrameest mis en pause lorsque l'onglet est inactif.

Résumé

Répétition des intervalles pour les onglets inactifs:

           setInterval      requestAnimationFrame 
Chrome
9- non affecté non pris en charge
10 non affecté en pause
11+> = 1000 ms en pause

Firefox
3- non affecté non pris en charge
4 non affectés 1s
5+> = 1000ms 2 n s (n = nombre d'images depuis l'inactivité)

C'EST À DIRE
9- non affecté non pris en charge
10+ non affectés mis en pause

Bord
13- non affecté en pause
14+> = 1000 ms en pause

Safari
5- non affecté non pris en charge
6 non affecté en pause
7+> = 1000 ms en pause

Opéra
12- non affecté non pris en charge
15+> = 1000 ms en pause
Antony
la source
Très bonne réponse. D'autres différences connues possibles pour des fonctions autres que setIntervalet requestAnimationFrame?
Andrew Mao
1
@AndrewMao Pas que je sache. Je suis tombé sur ce problème lorsque je travaillais sur une bibliothèque pour détecter de manière fiable si JS était réactivé avec setIntervalet requestAnimationFrame. Ce que je sais, c'est qu'il setTimeoutse comporte de la même manière setInterval, en ce sens qu'ils ont tous deux le même intervalle d'arrière-plan minimum dans Firefox et Chrome, et aucune limite apparente dans les autres navigateurs.
Antony
2
La valeur minimale de Firefox setInterval peut apparemment être modifiée en ouvrant l'URL about:configdans le navigateur et en changeant la dom.min_background_timeout_valuevaleur en autre chose que 1000.
Jonas Berlin
puis-je l'utiliser pour recharger la page toutes les 5 secondes lorsque le navigateur est minimisé?, voici ma question.
shaijut le
1
Notez que chrome ne met pas en pause / ne réduit pas la vitesse à laquelle requestAnimationFrameest appelée si l'utilisateur change simplement d'application (Alt + Tab hors de Chrome). Tant que l'onglet est actif dans Chrome, la "fréquence d'images" est plus ou moins constante.
Marc
11

Ce que j'ai observé: sur les onglets inactifs dans Chrome , toutes vos attentes setTimeout(doivent être identiques pour setInterval) moins de 1000 ms sont arrondies à 1000 ms . Je pense que les délais d'attente plus longs ne sont pas modifiés.

Semble être le comportement depuis Chrome 11 et Firefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

De plus, je ne pense pas que cela se comporte de cette façon lorsque la fenêtre entière est inactive (mais cela semble assez facile à étudier).


la source
1
jQuery focuset les blurévénements semblent détecter à la fois les changements d'onglet et de fenêtre, de sorte qu'il pourrait fonctionner dans les deux sens. Mais je me demande comment la fenêtre détecte si elle est réellement visible ou non.
Andrew Mao
2
En fait, il n'a aucun lien avec jQuery ou Javascript car il s'agit d'une implémentation interne du navigateur.
Pouvez-vous le confirmer maintenant fin 2016?
vsync
0

Une nouvelle réponse pour compléter celles-ci: sur chrome 78.0.3904.108, je remarque que tous ces délais (pas seulement ceux inférieurs à 1000 ms) prennent un peu plus de temps que prévu lorsque je passe à un onglet différent, puis que je reviens. Le comportement que je vois est plus correctement décrit comme "Tous les délais d'attente sur les onglets inactifs peuvent être retardés d'une certaine quantité supplémentaire, jusqu'à un maximum 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
la source