Chrome 88 (janvier 2021) limitera fortement les minuteurs JavaScript en série pour les pages masquées dans des conditions particulières. Cela réduit l'utilisation du processeur, ce qui réduit également l'utilisation de la batterie. Dans certains cas particuliers, cela peut modifier le comportement, mais les minuteurs sont souvent utilisés là où une API différente serait plus efficace et plus fiable.
OK, c'était assez jargonneux et un peu ambigu. Voyons cela ensemble.
Terminologie
Pages masquées
En général, masqué signifie qu'un autre onglet est actif ou que la fenêtre a été réduite, mais les navigateurs peuvent considérer qu'une page est masquée chaque fois que son contenu est totalement invisible. Certains navigateurs vont plus loin que d'autres, mais vous pouvez toujours utiliser l'API Page Visibility pour suivre quand le navigateur pense que la visibilité a changé.
Retardateurs JavaScript
Par délais JavaScript, j'entends setTimeout
et setInterval
, qui vous permettent de planifier un rappel à un moment donné. Les minuteurs sont utiles et ne vont pas disparaître, mais ils sont parfois utilisés pour interroger l'état lorsqu'un événement serait plus efficace et plus précis.
Minuteurs en série
Si vous appelez setTimeout
dans la même tâche qu'un rappel setTimeout
, la deuxième invocation est "enchaînée". Avec setInterval
, chaque itération fait partie de la chaîne. Cela peut être plus facile à comprendre avec du code:
let chainCount = 0;
setInterval(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
}, 500);
Et :
let chainCount = 0;
function setTimeoutChain() {
setTimeout(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
setTimeoutChain();
}, 500);
}
Fonctionnement du débit limité
Le débit est limité par étapes:
Limitation minimale
Cela se produit pour les minuteurs programmés lorsque l'une des conditions suivantes est remplie:
- La page est visible.
- La page a émis des bruits au cours des 30 dernières secondes. Il peut s'agir de n'importe quelle API de création de son, mais une piste audio silencieuse ne compte pas.
Le minuteur n'est pas limité, sauf si le délai avant expiration demandé est inférieur à 4 ms et que le nombre de chaînes est égal ou supérieur à cinq, auquel cas le délai avant expiration est défini sur 4 ms. Il ne s'agit pas d'une nouveauté. Les navigateurs font cela depuis de nombreuses années.
Limitations
Cela se produit pour les minuteurs programmés lorsque le bloquant minimal ne s'applique pas et que l'une des conditions suivantes est remplie:
- Le nombre de chaînes est inférieur à cinq.
- La page a été masquée pendant moins de cinq minutes.
- WebRTC est utilisé. Plus précisément, il existe un
RTCPeerConnection
avec unRTCDataChannel
"ouvert" ou unMediaStreamTrack
"en direct".
Le navigateur vérifie les minuteurs de ce groupe une fois par seconde. Étant donné qu'ils ne sont vérifiés qu'une fois par seconde, les minuteurs avec un délai avant expiration similaire seront regroupés, ce qui consolidera le temps dont l'onglet a besoin pour exécuter le code. Il ne s'agit pas non plus d'une nouveauté. Les navigateurs font cela dans une certaine mesure depuis des années.
Limitation intensive
OK, voici la nouveauté de Chrome 88. Le forçage intensif s'applique aux minuteurs planifiés lorsqu'aucune des conditions de limitation minimale ou de limitation ne s'applique et que toutes les conditions suivantes sont remplies:
- La page est masquée depuis plus de cinq minutes.
- Le nombre de chaînes est égal ou supérieur à cinq.
- La page est silencieuse depuis au moins 30 secondes.
- WebRTC n'est pas utilisé.
Dans ce cas, le navigateur vérifie les minuteurs de ce groupe une fois par minute. Comme auparavant, cela signifie que les minuteurs seront regroupés dans ces vérifications par minute.
Solutions
Il existe généralement une meilleure alternative à un minuteur, ou les minuteurs peuvent être combinés à autre chose pour être plus respectueux des processeurs et de l'autonomie de la batterie.
Interrogation de l'état
Il s'agit de l'utilisation (dé)mauvaise la plus courante des minuteurs, qui sont utilisés pour effectuer des vérifications ou des requêtes continues afin de voir si quelque chose a changé. Dans la plupart des cas, il existe un équivalent de push, où l'élément vous informe du changement lorsqu'il se produit, de sorte que vous n'ayez pas à vérifier en permanence. Vérifiez si un événement permet d'obtenir le même résultat.
Voici quelques exemples :
- Si vous devez savoir quand un élément entre dans la fenêtre d'affichage, utilisez
IntersectionObserver
. - Si vous devez savoir quand la taille d'un élément change, utilisez
ResizeObserver
. - Si vous devez savoir quand le DOM change, utilisez
MutationObserver
ou peut-être des appels de rappel du cycle de vie des éléments personnalisés. - Plutôt que d'interroger un serveur, envisagez d'utiliser des sockets Web, des événements envoyés par le serveur, des messages push ou des flux de récupération.
- Si vous devez réagir aux changements de scène dans l'audio/la vidéo, utilisez des événements tels que
timeupdate
etended
, ourequestVideoFrameCallback
si vous devez effectuer une action avec chaque frame.
Il existe également des déclencheurs de notification si vous souhaitez afficher une notification à un moment donné.
Animation
L'animation est un élément visuel. Elle ne doit donc pas utiliser de temps de processeur lorsque la page est masquée.
requestAnimationFrame
est beaucoup plus efficace pour planifier le travail d'animation que les minuteurs JavaScript. Il se synchronise avec la fréquence d'actualisation de l'appareil, ce qui garantit que vous ne recevez qu'un seul rappel par frame visible et que vous disposez du temps maximal pour construire ce frame. De plus, requestAnimationFrame
attend que la page soit visible. Il n'utilise donc aucun processeur lorsque la page est masquée.
Si vous pouvez déclarer l'intégralité de votre animation à l'avance, envisagez d'utiliser des animations CSS ou l'API Web Animations. Ils présentent les mêmes avantages que requestAnimationFrame
, mais le navigateur peut effectuer des optimisations supplémentaires telles que la composition automatique, et ils sont généralement plus faciles à utiliser.
Si votre animation est à faible fréquence d'images (comme un curseur clignotant), les minuteurs restent la meilleure option pour le moment, mais vous pouvez les combiner avec requestAnimationFrame
pour profiter des avantages des deux:
function animationInterval(ms, signal, callback) {
const start = document.timeline.currentTime;
function frame(time) {
if (signal.aborted) return;
callback(time);
scheduleFrame(time);
}
function scheduleFrame(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / ms) * ms;
const targetNext = start + roundedElapsed + ms;
const delay = targetNext - performance.now();
setTimeout(() => requestAnimationFrame(frame), delay);
}
scheduleFrame(start);
}
Utilisation :
const controller = new AbortController();
// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
console.log('tick!', time);
});
// And stop it:
controller.abort();
Tests
Cette modification sera activée pour tous les utilisateurs de Chrome dans Chrome 88 (janvier 2021). Elle est actuellement activée pour 50% des utilisateurs de Chrome Bêta, en développement et Canary. Si vous souhaitez le tester, utilisez l'indicateur de ligne de commande suivant lorsque vous lancez Chrome Bêta, Dev ou Canary:
--enable-features="IntensiveWakeUpThrottling:grace_period_seconds/10,OptOutZeroTimeoutTimersFromThrottling,AllowAggressiveThrottlingWithWebSocket"
L'argument grace_period_seconds/10
déclenche une limitation intense au bout de 10 secondes après la masquage de la page, au lieu de cinq minutes complètes, ce qui permet de voir plus facilement l'impact de la limitation.
L'avenir
Étant donné que les minuteurs sont une source d'utilisation excessive du processeur, nous allons continuer à chercher des moyens de les limiter sans endommager le contenu Web, ainsi que des API que nous pouvons ajouter/modifier pour répondre aux cas d'utilisation. Personnellement, j'aimerais éliminer le besoin de animationInterval
au profit de rappels d'animation à faible fréquence efficaces. Si vous avez des questions, n'hésitez pas à me contacter sur Twitter.
Photo de l'en-tête par Heather Zabriskie sur Unsplash.