1. Introduction
Démonstration interactive et atelier de programmation pour en savoir plus sur l'Interaction to Next Paint (INP)
Prérequis
- Connaissance du développement HTML et JavaScript
- Recommandation: lisez la documentation d'INP.
Objectifs
- comment l'interaction des interactions des utilisateurs et votre gestion de ces interactions affectent la réactivité de la page ;
- Comment réduire et éliminer les retards pour une expérience utilisateur fluide.
Ce dont vous avez besoin
- Un ordinateur permettant de cloner du code à partir de GitHub et d'exécuter des commandes npm
- Éditeur de texte.
- Une version récente de Chrome pour que toutes les mesures des interactions fonctionnent.
2. Configuration
Obtenir et exécuter le code
Le code se trouve dans le dépôt web-vitals-codelabs
.
- Clonez le dépôt dans votre terminal:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- Parcourez le répertoire cloné:
cd web-vitals-codelabs/understanding-inp
- Installer les dépendances:
npm ci
- Démarrer le serveur Web:
npm run start
- Accédez à l'adresse http://localhost:5173/understanding-inp/ dans votre navigateur.
Présentation de l'application
En haut de la page se trouvent un compteur Score et un bouton Incrémenter. Une démonstration classique de la réactivité et de la réactivité !
Quatre mesures figurent sous le bouton:
- INP: score INP actuel, qui est généralement la pire interaction.
- Interaction: score de l'interaction la plus récente.
- FPS: nombre d'images par seconde du thread principal de la page.
- Minuteur: animation du minuteur en cours d'exécution pour aider à visualiser les à-coups.
Les entrées FPS et Minuteur ne sont pas du tout nécessaires pour mesurer les interactions. Ils sont ajoutés simplement pour faciliter la visualisation de la réactivité.
Essayer
Essayez d'interagir avec le bouton Incrémenter et observez l'augmentation du score. Les valeurs INP et Interaction changent-elles à chaque incrément ?
L'INP mesure le temps qui s'écoule entre l'interaction de l'utilisateur et l'affichage de la mise à jour sur la page.
3. Mesurer les interactions avec les outils pour les développeurs Chrome
Ouvrez les outils de développement à partir du menu Plus d'outils > dans le menu Outils pour les développeurs, en effectuant un clic droit sur la page et en sélectionnant Inspecter, ou en utilisant un raccourci clavier.
Accédez au panneau Performances, que vous utiliserez pour mesurer les interactions.
Enregistrez ensuite une interaction dans le panneau "Performances".
- Appuyez sur "Enregistrer".
- Interagissez avec la page (appuyez sur le bouton Incrémenter).
- Arrêtez l'enregistrement.
Dans la chronologie qui s'affiche, vous trouverez une piste Interactions. Pour la développer, cliquez sur le triangle situé à gauche.
Deux interactions apparaissent. Faites un zoom avant sur le deuxième élément en faisant défiler la page ou en maintenant la touche W enfoncée.
En passant la souris sur l'interaction, vous pouvez constater que celle-ci a été rapide et que la durée du traitement n'a pas été prise en compte, et que le délai d'entrée et le délai de présentation ont été minimes, dont la durée exacte dépend de la vitesse de votre machine.
4. Écouteurs d'événements de longue durée
Ouvrez le fichier index.js
et annulez la mise en commentaire de la fonction blockFor
dans l'écouteur d'événements.
Afficher le code complet: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
Enregistrez le fichier. Le serveur va voir la modification et actualiser la page pour vous.
Réessayez d'interagir avec la page. Les interactions seront désormais nettement plus lentes.
Trace des performances
Effectuez un autre enregistrement dans le panneau "Performances" pour voir à quoi cela ressemble.
Ce qui était auparavant une interaction courte prend désormais une seconde complète.
Lorsque vous pointez sur l'interaction, vous remarquerez que le temps est presque entièrement consacré à la "Durée de traitement", qui correspond au temps nécessaire pour exécuter les rappels de l'écouteur d'événements. Étant donné que l'appel blockFor
bloquant se trouve entièrement dans l'écouteur d'événements, c'est là que le temps passe.
5. Test: durée de traitement
Essayez de réorganiser le travail de l'écouteur d'événements pour voir l'effet sur INP.
Commencez par mettre à jour l'UI
Que se passe-t-il si vous intervertissez l'ordre des appels JavaScript : mettez d'abord à jour l'interface utilisateur, puis bloquez-les ?
Afficher le code complet: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Avez-vous remarqué que l'interface utilisateur s'affiche plus tôt ? L'ordre affecte-t-il les scores INP ?
Essayez de prendre une trace et d'examiner l'interaction pour voir s'il y a des différences.
Écouteurs distincts
Que se passe-t-il si vous déplacez la tâche vers un écouteur d'événements distinct ? Mettez à jour l'UI dans un écouteur d'événements et bloquez la page dans un écouteur distinct.
Consulter le code complet: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
À quoi cela ressemble maintenant dans le panneau des performances ?
Différents types d'événements
La plupart des interactions déclenchent de nombreux types d'événements, qu'il s'agisse de pointeurs ou d'événements clés, d'un survol, d'un focus ou d'un floutage, et d'événements synthétiques tels que beforechange et beforeinput.
De nombreuses pages réelles ont des écouteurs pour de nombreux événements différents.
Que se passe-t-il si vous modifiez les types d'événements pour les écouteurs d'événements ? Par exemple, remplacez l'un des écouteurs d'événements click
par pointerup
ou mouseup
.
Consulter le code complet: diff_handlers.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Aucune mise à jour de l'interface utilisateur
Que se passe-t-il si vous supprimez l'appel de mise à jour de l'interface utilisateur de l'écouteur d'événements ?
Afficher le code complet: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
6. Résultats du test sur la durée de traitement
Trace des performances: commencez par mettre à jour l'UI
Afficher le code complet: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Si vous cliquez sur le bouton dans le panneau "Performances", les résultats n'ont pas changé. Alors qu'une mise à jour de l'interface utilisateur était déclenchée avant le code de blocage, le navigateur n'a mis à jour ce qui était affiché à l'écran qu'une fois l'écouteur d'événements terminé, ce qui signifie que l'interaction prenait encore un peu plus d'une seconde.
Trace des performances: écouteurs distincts
Consulter le code complet: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Là encore, il n'y a aucune différence fonctionnellement. L'interaction prend toujours une seconde entière.
Si vous faites un zoom avant sur l'interaction de clic, vous verrez que deux fonctions différentes sont appelées à la suite de l'événement click
.
Comme prévu, la première, qui consiste à mettre à jour l'interface utilisateur, s'exécute incroyablement vite, tandis que la seconde prend toute une seconde. Cependant, la somme de leurs effets produit la même interaction lente pour l'utilisateur final.
Trace des performances: différents types d'événements
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Ces résultats sont très similaires. L'interaction est toujours d'une seconde entière. La seule différence est que l'écouteur click
plus court de mise à jour de l'interface utilisateur s'exécute désormais après l'écouteur pointerup
bloquant.
Trace des performances: aucune mise à jour de l'UI
Afficher le code complet: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- Le score ne se met pas à jour, mais la page le fait malgré tout.
- Les animations, les effets CSS, les actions par défaut des composants Web (saisie de formulaire), la saisie de texte et la mise en surbrillance du texte continuent d'être mis à jour.
Dans ce cas, le bouton passe à un état actif et revient à l'état actif lorsque l'utilisateur clique dessus. Le navigateur doit alors repeindre le bouton, ce qui signifie qu'il reste un INP.
Étant donné que l'écouteur d'événements a bloqué le thread principal pendant une seconde, empêchant ainsi l'affichage de la page, l'interaction prend toujours une seconde entière.
L'enregistrement d'un panneau "Performances" montre que l'interaction est pratiquement identique à celle qui s'est produite auparavant.
Plats à emporter
Tout code exécuté dans n'importe quel écouteur d'événements retarde l'interaction.
- Cela inclut les écouteurs enregistrés à partir de différents scripts et du code du framework ou de la bibliothèque qui s'exécute dans les écouteurs, comme une mise à jour de l'état qui déclenche le rendu d'un composant.
- Pas seulement votre propre code, mais aussi tous les scripts tiers.
C'est un problème courant.
Enfin, le fait que votre code ne déclenche pas de peinture ne signifie pas qu'il n'attend pas que des écouteurs d'événements lents se terminent.
7. Test: délai d'entrée
Qu'en est-il du code de longue durée en dehors des écouteurs d'événements ? Exemple :
- Si un
<script>
de chargement tardif bloquait la page de manière aléatoire lors du chargement. - Un appel d'API, tel que
setInterval
, qui bloque régulièrement la page ?
Essayez de supprimer le blockFor
de l'écouteur d'événements et de l'ajouter à un setInterval()
:
Consulter le code complet: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Que se passe-t-il ?
8. Résultats du test avec délai d'entrée
Consulter le code complet: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
L'enregistrement d'un clic sur un bouton qui se produit pendant l'exécution de la tâche de blocage setInterval
entraîne une interaction de longue durée, même si aucun travail bloquant n'est effectué dans l'interaction elle-même.
Ces longues périodes sont souvent appelées tâches longues.
En passant la souris sur l'interaction dans les outils de développement, vous pouvez constater que la durée d'interaction est désormais principalement attribuée au délai d'entrée, et non à la durée du traitement.
Notez que cela n'affecte pas toujours les interactions. Si vous ne cliquez pas alors que la tâche est en cours d'exécution, vous pourriez avoir de la chance. Par exemple, elles sont "aléatoires" les éternuements peuvent être un cauchemar à déboguer lorsqu'ils ne causent que parfois des problèmes.
Pour les identifier, vous pouvez mesurer les longues tâches (ou longues images d'animation) et le temps total de blocage.
9. Présentation lente
Jusqu'à présent, nous avons examiné les performances de JavaScript via le délai d'entrée ou les écouteurs d'événements. Mais qu'est-ce qui affecte le rendu Next Paint ?
Eh bien, mettre à jour la page avec des effets coûteux !
Même si les pages sont mises à jour rapidement, le navigateur devra peut-être travailler dur pour les afficher !
Sur le thread principal:
- Frameworks d'UI qui doivent effectuer les mises à jour après un changement d'état
- Les modifications DOM ou l'activation/désactivation de nombreux sélecteurs de requêtes CSS coûteux peuvent déclencher de nombreuses modifications de style, de mise en page et de peinture.
En dehors du thread principal:
- Utiliser du CSS pour optimiser les effets GPU
- Ajouter de très grandes images haute résolution
- Utiliser le format SVG/Canvas pour dessiner des scènes complexes
Voici quelques exemples couramment utilisés sur le Web:
- Un site SPA qui reconstruit l'intégralité du DOM après avoir cliqué sur un lien, sans interrompre l'action pour fournir un premier retour visuel.
- Une page de recherche qui propose des filtres de recherche complexes avec une interface utilisateur dynamique, mais exécute des écouteurs coûteux pour le faire.
- Bouton d'activation/désactivation du mode sombre qui déclenche le style/la mise en page de la page entière
10. Test: retard de présentation
Périphérique requestAnimationFrame
lent
Simulons un long délai de présentation à l'aide de l'API requestAnimationFrame()
.
Déplacez l'appel blockFor
dans un rappel requestAnimationFrame
afin qu'il s'exécute après le renvoi de l'écouteur d'événements:
Consulter le code complet: overview_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Que se passe-t-il ?
11. Résultats du test du délai de présentation
Consulter le code complet: overview_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
L'interaction dure une seconde. Que s'est-il donc passé ?
requestAnimationFrame
demande un rappel avant la peinture suivante. Étant donné que l'INP mesure le temps écoulé entre l'interaction et le rendu suivant, le blockFor(1000)
de requestAnimationFrame
continue de bloquer le pain suivant pendant une seconde entière.
Notez toutefois deux points:
- En pointant sur l'écran, vous verrez que la totalité du temps d'interaction est maintenant dépensée dans "Délai de présentation". puisque le blocage du thread principal se produit après le retour de l'écouteur d'événements.
- La racine de l'activité du thread principal n'est plus l'événement de clic, mais "Frame d'animation déclenché".
12. Diagnostiquer les interactions
Sur cette page de test, la réactivité est super visuelle, avec les scores, les minuteurs et l'interface utilisateur du compteur. Mais lors du test de la page moyenne, elle est plus subtile.
Lorsque les interactions durent longtemps, il n'est pas toujours évident d'identifier le coupable. Est-ce:
- Délai d'entrée ?
- Durée du traitement des événements ?
- Un retard dans la présentation ?
Sur n'importe quelle page, vous pouvez utiliser les outils de développement pour mesurer la réactivité. Pour prendre cette habitude, essayez ce processus:
- Naviguez sur le Web comme vous le feriez habituellement.
- Facultatif: Laissez la console des outils de développement ouverte pendant que l'extension Web Vitals consigne les interactions.
- Si vous constatez qu'une interaction n'est pas performante, essayez de la reproduire:
- Si vous ne pouvez pas le répéter, utilisez les journaux de la console pour obtenir des insights.
- Si vous pouvez le répéter, enregistrez-le dans le panneau des performances.
Tous les retards
Essayez d'ajouter à la page quelques-uns de ces problèmes:
Consulter le code complet: all_the_things.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Utilisez ensuite la console et le panneau "Performances" pour diagnostiquer les problèmes.
13. Test: travail asynchrone
Étant donné que vous pouvez lancer des effets non visuels dans les interactions, comme effectuer des requêtes réseau, démarrer des minuteurs ou simplement mettre à jour l'état global, que se passe-t-il lorsque ces effets finissent par mettre à jour la page ?
Tant que l'élément next paint suivant une interaction est autorisé à s'afficher, même si le navigateur décide qu'il n'a pas réellement besoin d'une nouvelle mise à jour de l'affichage, la mesure de l'interaction s'arrête.
Pour essayer, continuez à mettre à jour l'interface utilisateur à partir de l'écouteur de clics, mais exécutez la tâche bloquante à partir du délai avant expiration.
Consulter le code complet: expiration_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Que va-t-il se passer ?
14. Résultats des tests de travail asynchrones
Consulter le code complet: expiration_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
L'interaction est désormais courte, car le thread principal est disponible immédiatement après la mise à jour de l'UI. La longue tâche de blocage s'exécute toujours après l'application de la peinture. L'utilisateur reçoit donc immédiatement un retour d'information sur l'interface utilisateur.
Leçon: Si tu n'arrives pas à la supprimer, tu dois au moins la déplacer !
Méthodes
Peut-on faire mieux qu'un setTimeout
fixe de 100 millisecondes ? Nous voulons probablement que le code s'exécute aussi rapidement que possible, sinon nous aurions dû le supprimer !
Objectif :
- L'interaction sera exécutée
incrementAndUpdateUI()
. blockFor()
s'exécutera dès que possible, mais ne bloquera pas la prochaine peinture.- Il en résulte un comportement prévisible sans "délais d'inactivité magiques".
Voici quelques façons d'y parvenir:
setTimeout(0)
Promise.then()
requestAnimationFrame
requestIdleCallback
scheduler.postTask()
"requestPostAnimationFrame"
Contrairement à requestAnimationFrame
seul (qui tentera de s'exécuter avant le prochain pain et produirait généralement une interaction lente), requestAnimationFrame
+ setTimeout
constitue un polyfill simple pour requestPostAnimationFrame
, qui exécute le rappel après le prochain pain.
Afficher le code complet: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Pour des raisons d'ergonomie, vous pouvez même tenir toutes vos promesses:
Afficher le code complet: raf+task2.html
async function nextPaint() {
return new Promise(resolve => afterNextPaint(resolve));
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await nextPaint();
blockFor(1000);
});
15. Interactions multiples (et clics furtifs)
Il peut être utile de déplacer de longues solutions de blocage, mais elles bloquent tout de même la page, ce qui affecte les futures interactions, ainsi que de nombreuses autres animations et mises à jour de la page.
Essayez à nouveau d'utiliser la version de travail avec blocage asynchrone de la page (ou la vôtre si vous avez créé votre propre version sur le report des tâches à la dernière étape):
Consulter le code complet: expiration_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Que se passe-t-il si vous cliquez plusieurs fois rapidement ?
Trace des performances
Pour chaque clic, une tâche d'une seconde est mise en file d'attente, ce qui garantit que le thread principal est bloqué pendant une durée importante.
Lorsque ces longues tâches se chevauchent avec l'arrivée de nouveaux clics, les interactions sont lentes, même si l'écouteur d'événements lui-même est renvoyé presque immédiatement. Nous avons créé la même situation que dans le test précédent concernant les délais de saisie. Cette fois, le délai d'entrée ne provient pas d'un élément setInterval
, mais d'une tâche déclenchée par des écouteurs d'événements antérieurs.
Stratégies
Idéalement, nous voulons supprimer complètement les longues tâches !
- Supprimez complètement le code inutile, en particulier les scripts.
- Optimiser le code pour éviter d'exécuter de longues tâches
- Annuler les tâches obsolètes lorsque de nouvelles interactions se produisent.
16. Stratégie 1: contre-rebond
Une stratégie classique. Chaque fois que des interactions se succèdent rapidement et que le traitement ou les effets de réseau sont coûteux, retardez volontairement le démarrage du travail afin que vous puissiez l'annuler et le redémarrer. Ce modèle est utile pour les interfaces utilisateur telles que les champs de saisie semi-automatique.
- Utilisez
setTimeout
pour retarder le démarrage d'une tâche coûteuse, avec un minuteur, de 500 à 1 000 millisecondes par exemple. - Enregistrez l'identifiant du minuteur lors de cette opération.
- Si une nouvelle interaction se produit, annulez le minuteur précédent avec
clearTimeout
.
Consulter le code complet: debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
Trace des performances
Malgré plusieurs clics, une seule tâche blockFor
finit par s'exécuter, en attendant qu'il n'y ait plus eu de clics pendant une seconde entière avant de s'exécuter. Pour les interactions intensives, comme la saisie de texte ou les éléments cibles susceptibles de générer plusieurs clics rapides, il s'agit de la stratégie idéale à utiliser par défaut.
17. Stratégie 2: interrompre les tâches de longue durée
Il est toutefois possible qu'un autre clic se produise juste après la fin du délai de réponse, qu'il se retrouve au milieu de cette longue tâche et qu'il devienne une interaction très lente en raison du délai d'entrée.
Idéalement, si une interaction intervient au milieu de notre tâche, nous souhaitons mettre en pause notre travail chargé afin que toute nouvelle interaction soit traitée immédiatement. Comment pouvons-nous y parvenir ?
Il existe des API comme isInputPending
, mais il est généralement préférable de diviser les longues tâches en fragments.
Beaucoup de setTimeout
Première tentative: faites quelque chose de simple.
Consulter le code complet: small_tasks.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
});
});
Cela permet au navigateur de planifier chaque tâche individuellement, et la saisie peut être plus prioritaire.
Nous sommes revenus à un travail de cinq secondes complet pour cinq clics, mais chaque tâche d'une seconde par clic a été divisée en dix tâches de 100 millisecondes. Par conséquent, même si plusieurs interactions se chevauchent avec ces tâches, aucune interaction ne présente de délai d'entrée supérieur à 100 millisecondes ! Le navigateur donne la priorité aux écouteurs d'événements entrants par rapport au travail setTimeout
, et les interactions restent responsives.
Cette stratégie fonctionne particulièrement bien pour planifier des points d'entrée distincts, par exemple si vous devez appeler de nombreuses fonctionnalités indépendantes au moment du chargement de l'application. Par défaut, il suffit de charger les scripts et de tout exécuter au moment de leur évaluation.
Cependant, cette stratégie ne fonctionne pas aussi bien pour décomposer du code étroitement couplé, comme une boucle for
qui utilise un état partagé.
Maintenant avec yield()
Toutefois, nous pouvons utiliser les async
et await
modernes pour ajouter facilement des "points de rendement" à n'importe quelle fonction JavaScript.
Exemple :
Consulter le code complet: rendementy.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldy(ms) {
const ms_per_part = 10;
const parts = ms / ms_per_part;
for (let i = 0; i < parts; i++) {
await schedulerDotYield();
blockFor(ms_per_part);
}
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await blockInPiecesYieldy(1000);
});
Comme précédemment, le thread principal est généré après un bloc de travail et le navigateur est en mesure de répondre à toutes les interactions entrantes. Toutefois, il suffit désormais d'un await schedulerDotYield()
au lieu de setTimeout
distinctes, ce qui le rend suffisamment ergonomique pour être utilisé même au milieu d'une boucle for
.
Maintenant avec AbortContoller()
Cela a fonctionné, mais chaque interaction planifie davantage de travail, même si de nouvelles interactions sont arrivées et ont pu changer le travail qui doit être fait.
Avec cette stratégie, nous annulons le délai avant expiration précédent à chaque nouvelle interaction. Pouvons-nous faire quelque chose de similaire ici ? Pour ce faire, vous pouvez utiliser un AbortController()
:
Afficher le code complet: abandonné.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldyAborty(ms, signal) {
const parts = ms / 10;
for (let i = 0; i < parts; i++) {
// If AbortController has been asked to stop, abandon the current loop.
if (signal.aborted) return;
await schedulerDotYield();
blockFor(10);
}
}
let abortController = new AbortController();
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
abortController.abort();
abortController = new AbortController();
await blockInPiecesYieldyAborty(1000, abortController.signal);
});
Lorsqu'un clic se produit, il lance la boucle for
blockInPiecesYieldyAborty
pour effectuer le travail nécessaire, tout en générant régulièrement le thread principal afin que le navigateur reste réactif aux nouvelles interactions.
Lorsqu'un second clic se produit, la première boucle est signalée comme annulée avec AbortController
et une nouvelle boucle blockInPiecesYieldyAborty
est lancée. La prochaine fois que la première boucle est planifiée pour s'exécuter à nouveau, elle remarque que signal.aborted
est désormais true
et est immédiatement renvoyé sans effectuer d'autres tâches.
18. Conclusion
La séparation de toutes les longues tâches permet au site d'être réactif aux nouvelles interactions. Cela vous permet de fournir rapidement des commentaires initiaux et de prendre des décisions, par exemple pour annuler un travail en cours. Cela implique parfois de planifier les points d'entrée en tant que tâches distinctes. Parfois, cela implique d'ajouter la colonne "rendement" lorsque cela est pratique.
À noter
- INP mesure toutes les interactions.
- Chaque interaction est mesurée entre l'entrée et la peinture suivante, c'est-à-dire la façon dont l'utilisateur voit la réactivité.
- Le délai d'entrée, la durée de traitement des événements et le délai de présentation ont tous une incidence sur la réactivité de l'interaction.
- Avec les outils de développement, vous pouvez facilement mesurer l'INP et la répartition des interactions.
Stratégies
- Évitez les longues tâches de code (c'est-à-dire les longues tâches) sur vos pages.
- Retirez le code inutile des écouteurs d'événements jusqu'à la fin du processus.
- Assurez-vous que la mise à jour du rendu est efficace pour le navigateur.