1. Introduction
Une démo interactive et un atelier de programmation pour découvrir l'Interaction to Next Paint (INP).
Prérequis
- Connaissance du développement HTML et JavaScript.
- Recommandation : lisez la documentation sur l'INP.
Objectifs
- Comment l'interaction entre les actions de l'utilisateur et la façon dont vous les gérez affecte 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 capable de cloner du code depuis GitHub et d'exécuter des commandes npm.
- Un éditeur de texte.
- Une version récente de Chrome pour que toutes les mesures d'interaction 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 - Accédez au répertoire cloné :
cd web-vitals-codelabs/understanding-inp - Installez les dépendances :
npm ci - Démarrez le serveur Web :
npm run start - Accédez à 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 Increment (Incrémenter). Une démonstration classique de réactivité et de réactivité !

Sous le bouton, quatre mesures sont affichées :
- INP : score INP actuel, qui correspond 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 de minuteur en cours d'exécution pour aider à visualiser les à-coups.
Les entrées FPS et Timer ne sont pas du tout nécessaires pour mesurer les interactions. Ils sont ajoutés uniquement pour faciliter la visualisation de la responsivité.
Essayer
Essayez d'interagir avec le bouton Increment (Incrémenter) et observez l'augmentation du score. Est-ce que les valeurs INP et Interaction changent à chaque incrément ?
L'INP mesure le temps qui s'écoule entre le moment où l'utilisateur interagit et celui où la page affiche la mise à jour affichée à l'utilisateur.
3. Mesurer les interactions avec les Outils pour les développeurs Chrome
Ouvrez les outils de développement depuis le menu Plus d'outils > Outils de développement, 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.

Ensuite, capturez 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. Développez-le en cliquant sur le triangle à gauche.

Deux interactions s'affichent. Faites un zoom avant sur le deuxième en faisant défiler l'écran ou en maintenant la touche W enfoncée.

En pointant sur l'interaction, vous pouvez voir qu'elle a été rapide, qu'elle n'a pas pris de temps dans la durée de traitement et qu'elle a pris un minimum de temps dans le délai d'entrée et le délai de présentation, 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.
Voir le code complet : click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
Enregistrez le fichier. Le serveur détectera la modification et actualisera la page pour vous.
Essayez d'interagir à nouveau avec la page. Les interactions seront désormais nettement plus lentes.
Trace de performances
Effectuez un autre enregistrement dans le panneau "Performances" pour voir à quoi cela ressemble.

Ce qui n'était qu'une brève interaction prend désormais une seconde entière.
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 est consacré.
5. Test : durée de traitement
Essayez de réorganiser le travail de l'écouteur d'événements pour voir l'effet sur l'INP.
Mettre d'abord à jour l'UI
Que se passe-t-il si vous inversez l'ordre des appels JS (mettre à jour l'UI d'abord, puis bloquer) ?
Voir le code complet : ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Avez-vous remarqué que l'UI s'affichait plus tôt ? L'ordre a-t-il une incidence sur les scores INP ?
Essayez d'effectuer 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 le travail vers un écouteur d'événements distinct ? Mettez à jour l'interface utilisateur dans un écouteur d'événements et bloquez la page à partir d'un autre écouteur.
Voir le code complet : two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
À quoi ressemble-t-il maintenant dans le panneau "Performances" ?
Différents types d'événements
La plupart des interactions déclenchent de nombreux types d'événements, des événements de pointeur ou de touche aux événements de survol, de focus/flou et 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, remplacer l'un des écouteurs d'événements click par pointerup ou mouseup ?
Voir le code complet : diff_handlers.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Aucune mise à jour de l'UI
Que se passe-t-il si vous supprimez l'appel de mise à jour de l'UI de l'écouteur d'événements ?
Voir 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 : mettre d'abord à jour l'UI
Voir le code complet : ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
En examinant un enregistrement du panneau "Performances" après avoir cliqué sur le bouton, vous pouvez constater que les résultats n'ont pas changé. Bien qu'une mise à jour de l'UI ait été déclenchée avant le code bloquant, le navigateur n'a pas mis à jour ce qui était affiché à l'écran avant la fin de l'écouteur d'événements. Cela signifie que l'interaction a tout de même pris un peu plus d'une seconde.

Trace de performances : écouteurs distincts
Voir le code complet : two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Encore une fois, il n'y a aucune différence fonctionnelle. L'interaction prend toujours une seconde complète.
Si vous effectuez un zoom avant sur l'interaction de clic, vous verrez que deux fonctions différentes sont effectivement appelées à la suite de l'événement click.
Comme prévu, la première (mise à jour de l'UI) s'exécute très rapidement, tandis que la seconde prend une seconde complète. Toutefois, la somme de leurs effets entraîne la même interaction lente pour l'utilisateur final.

Trace de 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 dure toujours une seconde complète. La seule différence est que l'écouteur click plus court, qui ne met à jour que l'UI, s'exécute désormais après l'écouteur pointerup bloquant.

Trace des performances : aucune mise à jour de l'UI
Voir le code complet : no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- Le score ne s'actualise pas, mais la page, si !
- 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 à l'état actif, puis revient à l'état initial lorsque l'utilisateur clique dessus. Cela nécessite un paint par le navigateur, ce qui signifie qu'il y a toujours un INP.
Comme l'écouteur d'événements a bloqué le thread principal pendant une seconde, empêchant l'affichage de la page, l'interaction prend toujours une seconde complète.
L'enregistrement du panneau "Performance" montre que l'interaction est pratiquement identique à celles qui l'ont précédée.

Plats à emporter
Tout code exécuté dans un écouteur d'événements retardera l'interaction.
- Cela inclut les écouteurs enregistrés à partir de différents scripts et du code de framework ou de bibliothèque qui s'exécute dans les écouteurs, comme une mise à jour d'état qui déclenche l'affichage d'un composant.
- Cela s'applique non seulement à votre propre code, mais aussi à tous les scripts tiers.
C'est un problème courant.
Enfin, ce n'est pas parce que votre code ne déclenche pas de peinture qu'une peinture n'attendra pas que les écouteurs d'événements lents se terminent.
7. Test : délai de réponse à l'entrée utilisateur
Qu'en est-il du code de longue durée en dehors des écouteurs d'événements ? Exemple :
- Si vous aviez un
<script>à chargement tardif qui bloquait aléatoirement la page pendant le chargement. - Un appel d'API, tel que
setInterval, qui bloque régulièrement la page ?
Essayez de supprimer blockFor de l'écouteur d'événements et de l'ajouter à un setInterval() :
Voir 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 sur le délai de réponse à l'entrée utilisateur
Voir 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 bloquante setInterval entraîne une interaction de longue durée, même si aucune tâche bloquante n'est effectuée dans l'interaction elle-même.
Ces périodes de longue durée sont souvent appelées "tâches longues".
En pointant sur l'interaction dans les outils de développement, vous constaterez que le temps d'interaction est désormais principalement attribué au délai d'entrée, et non à la durée de traitement.

Notez que cela n'affecte pas toujours les interactions. Si vous ne cliquez pas pendant l'exécution de la tâche, vous aurez peut-être de la chance. Ces "éternuements" aléatoires peuvent être un cauchemar à déboguer lorsqu'ils ne causent des problèmes que de temps en temps.
Pour les identifier, vous pouvez mesurer les tâches longues (ou les frames d'animation longs) et le Total Blocking Time.
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 d'autre le rendu du prochain affichage ?
En mettant à jour la page avec des effets coûteux.
Même si la mise à jour de la page est rapide, le navigateur peut encore avoir du mal à l'afficher.
Sur le thread principal :
- Frameworks d'UI qui doivent afficher les mises à jour après les changements d'état
- Les modifications du DOM ou l'activation de nombreux sélecteurs de requêtes CSS coûteux peuvent déclencher de nombreuses opérations de style, de mise en page et d'affichage.
En dehors du thread principal :
- Utiliser CSS pour optimiser les effets GPU
- Ajouter des images haute résolution de très grande taille
- Utiliser SVG/Canvas pour dessiner des scènes complexes

Voici quelques exemples courants sur le Web :
- Un site SPA qui recrée l'intégralité du DOM après un clic sur un lien, sans s'arrêter pour fournir un retour visuel initial.
- Une page de recherche qui propose des filtres de recherche complexes avec une interface utilisateur dynamique, mais qui exécute des écouteurs coûteux pour ce faire.
- Un bouton d'activation/de désactivation du mode sombre qui déclenche le style/la mise en page de l'ensemble de la page
10. Test : délai 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 :
Voir le code complet : presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Que se passe-t-il ?
11. Résultats du test sur le délai de présentation
Voir le code complet : presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
L'interaction dure toujours une seconde. Alors, que s'est-il passé ?
requestAnimationFrame demande un rappel avant le prochain paint. Étant donné que l'INP mesure le temps écoulé entre l'interaction et le prochain paint, le blockFor(1000) dans le requestAnimationFrame continue de bloquer le prochain paint pendant une seconde complète.

Notez toutefois deux choses :
- En pointant sur l'élément, vous verrez que toute la durée d'interaction est désormais consacrée au "délai de présentation", car 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 "Animation Frame Fired" (Frame d'animation déclenchée).
12. Diagnostiquer les interactions
Sur cette page de test, la réactivité est très visuelle, avec les scores, les minuteurs et l'interface utilisateur du compteur, mais lorsque l'on teste une page moyenne, elle est plus subtile.
Lorsque les interactions sont longues, il n'est pas toujours facile d'en identifier la cause. Est-ce :
- Délai de réponse à l'entrée utilisateur ?
- Durée de traitement des événements ?
- Délai de présentation ?
Sur n'importe quelle page, vous pouvez utiliser les outils de développement pour mesurer la réactivité. Pour prendre l'habitude, essayez ce flux :
- Naviguez sur le Web comme vous le feriez normalement.
- Surveillez le journal des interactions dans la vue des métriques en direct du panneau "Performances" des outils de développement.
- Si vous constatez qu'une interaction est peu performante, essayez de la répéter :
- Si vous ne parvenez pas à le reproduire, utilisez le journal des interactions pour obtenir des informations.
- Si vous pouvez le répéter, enregistrez une trace dans le panneau "Performances".
Tous les retards
Essayez d'ajouter un peu de tous ces problèmes à la page :
Voir 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 : tâches asynchrones
Puisque 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 le prochain paint après une interaction est autorisé à s'afficher, même si le navigateur décide qu'il n'a pas besoin d'une nouvelle mise à jour de l'affichage, la mesure de l'interaction s'arrête.
Pour tester cela, continuez à mettre à jour l'UI à partir de l'écouteur de clics, mais exécutez la tâche bloquante à partir du délai avant expiration.
Voir le code complet : timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Que va-t-il se passer ?
14. Résultats du test sur les tâches asynchrones
Voir le code complet : timeout_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 tâche de blocage longue s'exécute toujours, mais elle s'exécute un peu après l'affichage, de sorte que l'utilisateur reçoit un retour immédiat de l'UI.
Leçon : si vous ne pouvez pas le supprimer, déplacez-le au moins !
Méthodes
Pouvons-nous faire mieux qu'un setTimeout fixe de 100 millisecondes ? Nous voulons probablement toujours que le code s'exécute le plus rapidement possible, sinon nous l'aurions simplement supprimé.
Objectif :
- L'interaction s'exécutera
incrementAndUpdateUI(). blockFor()s'exécutera dès que possible, mais ne bloquera pas le prochain paint.- Cela permet d'obtenir un comportement prévisible sans "délais d'expiration magiques".
Pour ce faire, vous pouvez :
setTimeout(0)Promise.then()requestAnimationFramerequestIdleCallbackscheduler.postTask()
"requestPostAnimationFrame"
Contrairement à requestAnimationFrame seul (qui tente de s'exécuter avant le prochain paint et qui ralentit généralement l'interaction), requestAnimationFrame + setTimeout constitue un polyfill simple pour requestPostAnimationFrame, qui exécute le rappel après le prochain paint.
Voir le code complet : raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Pour plus d'ergonomie, vous pouvez même l'encapsuler dans une promesse :
Voir 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 de rage)
Déplacer les tâches bloquantes de longue durée peut aider, mais ces tâches longues bloquent toujours la page, ce qui affecte les interactions futures ainsi que de nombreuses autres animations et mises à jour de la page.
Réessayez la version de la page avec blocage asynchrone (ou la vôtre si vous avez trouvé votre propre variante pour différer le travail à l'étape précédente) :
Voir le code complet : timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Que se passe-t-il si vous cliquez plusieurs fois rapidement ?
Trace de 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 un certain temps.

Lorsque ces tâches longues se chevauchent avec de nouveaux clics, les interactions sont lentes, même si l'écouteur d'événements lui-même renvoie un résultat presque immédiatement. Nous avons recréé la même situation que dans l'expérience précédente avec des retards d'entrée. Sauf que cette fois, le délai d'entrée ne provient pas d'un setInterval, mais d'un travail déclenché par des écouteurs d'événements antérieurs.
Stratégies
Idéalement, nous voulons supprimer complètement les tâches longues.
- Supprimez complètement le code inutile, en particulier les scripts.
- Optimisez le code pour éviter d'exécuter des tâches longues.
- Abandonner les tâches obsolètes lorsque de nouvelles interactions arrivent.
16. Stratégie 1 : anti-rebond
Une stratégie classique. Chaque fois que des interactions se succèdent rapidement et que les effets de traitement ou de réseau sont coûteux, retardez volontairement le démarrage du travail afin de pouvoir annuler et redémarrer. Ce modèle est utile pour les interfaces utilisateur telles que les champs de saisie semi-automatique.
- Utilisez
setTimeoutpour retarder le démarrage d'une tâche coûteuse, avec un minuteur, par exemple de 500 à 1 000 millisecondes. - Enregistrez l'ID du minuteur lorsque vous le faites.
- Si une nouvelle interaction se produit, annulez le minuteur précédent à l'aide de
clearTimeout.
Voir le code complet : debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
Trace de performances

Malgré plusieurs clics, une seule tâche blockFor finit par s'exécuter, en attendant qu'il n'y ait plus de clics pendant une seconde complète avant de s'exécuter. Pour les interactions qui se produisent par rafales, comme la saisie dans un champ de texte ou les cibles d'éléments censées recevoir plusieurs clics rapides, il s'agit d'une stratégie idéale à utiliser par défaut.
17. Stratégie 2 : Interrompre les travaux à exécution longue
Il est toujours possible qu'un autre clic se produise juste après la période de debounce, qu'il se retrouve au milieu de cette tâche longue et qu'il devienne une interaction très lente en raison du délai d'entrée.
Idéalement, si une interaction se produit au milieu de notre tâche, nous voulons mettre en pause notre travail en cours afin que toutes les nouvelles interactions soient traitées immédiatement. Comment faire ?
Il existe des API comme isInputPending, mais il est généralement préférable de diviser les tâches longues en plusieurs parties.
Beaucoup de setTimeout
Première tentative : faire quelque chose de simple.
Voir 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);
});
});
Le navigateur peut ainsi planifier chaque tâche individuellement, et l'entrée peut être prioritaire.

Nous sommes revenus à cinq secondes de travail 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 n'a de délai d'entrée supérieur à 100 millisecondes. Le navigateur donne la priorité aux écouteurs d'événements entrants par rapport à la tâche setTimeout, et les interactions restent réactives.
Cette stratégie fonctionne particulièrement bien lorsque vous planifiez des points d'entrée distincts, par exemple si vous avez un ensemble de fonctionnalités indépendantes que vous devez appeler au moment du chargement de l'application. Le simple fait de charger des scripts et de tout exécuter au moment de l'évaluation du script peut entraîner l'exécution de l'ensemble dans une tâche longue géante par défaut.
Toutefois, cette stratégie ne fonctionne pas aussi bien pour décomposer du code fortement couplé, comme une boucle for qui utilise un état partagé.
Maintenant avec yield()
Toutefois, nous pouvons exploiter les async et await modernes pour ajouter facilement des "points limites" à n'importe quelle fonction JavaScript.
Exemple :
Voir le code complet : yieldy.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 cédé après un bloc de travail et le navigateur peut répondre à toutes les interactions entrantes. Toutefois, il suffit désormais d'un await schedulerDotYield() au lieu de setTimeout distincts, 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 plus de travail, même si de nouvelles interactions sont arrivées et ont peut-être modifié le travail à effectuer.
Avec la stratégie de débourrage, nous avons annulé 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() :
Voir le code complet : aborty.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 est enregistré, il lance la boucle blockInPiecesYieldyAborty for, qui effectue toutes les tâches nécessaires tout en cédant régulièrement le thread principal pour que le navigateur reste réactif aux nouvelles interactions.
Lorsqu'un deuxième clic est effectué, 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 renvoie immédiatement sans effectuer d'autres tâches.

18. Conclusion
La division de toutes les tâches longues permet à un site de répondre aux nouvelles interactions. Cela vous permet de fournir rapidement des commentaires initiaux et de prendre des décisions, comme l'annulation d'un travail en cours. Parfois, cela signifie qu'il faut planifier les points d'entrée en tant que tâches distinctes. Cela signifie parfois qu'il faut ajouter des points "yield" là où cela est pratique.
À noter
- L'INP mesure toutes les interactions.
- Chaque interaction est mesurée de l'entrée au prochain paint, c'est-à-dire la façon dont l'utilisateur perçoit la réactivité.
- Le délai de réponse à l'entrée utilisateur, la durée de traitement des événements et le délai de présentation ont tous une incidence sur la réactivité des interactions.
- Vous pouvez facilement mesurer l'INP et les répartitions des interactions avec les outils de développement.
Stratégies
- Évitez d'avoir du code de longue durée (tâches longues) sur vos pages.
- Déplacez le code inutile hors des écouteurs d'événements jusqu'après le prochain rendu.
- Assurez-vous que la mise à jour du rendu est efficace pour le navigateur.