Comment rendre la main au thread principal pour améliorer l'INP

Utilisez scheduler.yield() pour fractionner les tâches longues et maintenir la réactivité de vos pages

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-04

Rendre la main au thread principal

Imaginez un film romantique. Le décor est un petit marché français au centre d'un petit village. Les rues sont remplies de couples buvant du café, mangeant des croissants et achetant des fleurs. Imaginez maintenant ce qui se passe si un seul acheteur peut effectuer un achat auprès d'un vendeur à la fois, tandis que tous les autres doivent attendre leur tour. Le boulanger est submergé de demandes, des disputes éclatent chez le fleuriste et la promenade romantique se transforme en une attente frustrante.

Eh bien... c'est un peu ce qui se passe sur un site web lorsque les choses deviennent trop chargées.

Dernière révision par [url=https:\/\/www.linkedin.com\/in\/arjenkarel\/]Arjen Karel[\/url] en mars 2026

Pourquoi rendre la main (yielding) est important pour l'INP

Le thread principal d'un navigateur gère tous les processus importants : l'analyse du HTML et du CSS, l'exécution du JavaScript, la gestion des événements d'entrée comme les clics et les défilements, et le rendu visuel. Il fonctionne sur un modèle mono-thread, ce qui signifie qu'il ne peut effectuer qu'une seule tâche à la fois. Lorsqu'une tâche commence à s'exécuter, le navigateur l'exécute jusqu'à son terme et ne s'arrête pas avant qu'elle ne soit finie. Aucune autre tâche n'est planifiée tant que la tâche actuelle n'est pas terminée. C'est ce qu'on appelle bloquer le thread principal.

Le blocage du thread principal est la cause première des mauvais scores [url=\/core-web-vitals\/interaction-to-next-paint]Interaction to Next Paint (INP)[\/url]. Lorsqu'un visiteur clique sur un bouton et que votre JavaScript bloque le thread principal pendant 200 ms, le navigateur ne peut pas afficher de réponse tant que le script n'est pas terminé. La phase de [url=\/core-web-vitals\/interaction-to-next-paint\/processing-time]processing time[\/url] de l'INP mesure précisément ce délai. Selon le [url=https:\/\/almanac.httparchive.org\/en\/2025\/performance]Web Almanac 2025[\/url], le temps de blocage total (Total Blocking Time) mobile médian est de 1 916 ms, en hausse de 58 % par rapport à l'année précédente. Cela représente près de 2 secondes pendant lesquelles le navigateur ne peut pas répondre aux entrées de l'utilisateur.

Une façon de corriger cela est de rendre la main (yielding) au thread principal. Le yielding est une technique où les tâches longues sont fractionnées en plusieurs tâches plus petites pour permettre au navigateur de gérer des travaux plus importants (comme répondre aux entrées de l'utilisateur) entre elles.

Tâches longues et période de blocage : lorsqu'une tâche prend plus de 50 millisecondes, elle est classée comme une tâche longue, et tout ce qui dépasse ce seuil de 50 millisecondes est appelé "période de blocage" de la tâche. Fractionner ces tâches longues en morceaux plus petits permet au navigateur de rester réactif, même lors de la gestion d'opérations gourmandes en calcul.

Anciennes stratégies de yielding

Avant l'[url=https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Prioritized_Task_Scheduling_API]API Prioritized Task Scheduling[\/url], il existait 4 façons de rendre la main au thread principal. Toutes ont des limites.

  • setTimeout() : la stratégie la plus courante. setTimeout() avec un délai de 0 ajoute la tâche à la fin de la file d'attente, permettant aux autres tâches de s'exécuter en premier. Le problème : les tâches ne peuvent être poussées qu'à la fin de la file d'attente, de sorte que d'autres scripts peuvent passer devant avant que votre code ne reprenne. Les appels setTimeout() imbriqués imposent également un délai minimum de 5 ms après cinq rounds.
  • requestAnimationFrame() : place une fonction en file d'attente pour qu'elle s'exécute avant le prochain rafraîchissement (repaint) du navigateur. Souvent combiné avec setTimeout() pour s'assurer que les rappels (callbacks) sont planifiés après la prochaine mise à jour de la mise en page (layout). Pas conçu pour le yielding, mais pour le travail d'animation.
  • requestIdleCallback() : mieux adapté aux tâches non critiques et de faible priorité qui peuvent s'exécuter pendant les temps d'arrêt du navigateur. La limite : il n'y a aucune garantie que les tâches planifiées s'exécuteront rapidement (ou même du tout) sur un thread principal occupé.
  • isInputPending() : vérifie les entrées utilisateur en attente et ne rend la main que si une entrée est détectée. [url=https:\/\/web.dev\/articles\/optimize-long-tasks]Google ne recommande plus cette approche[\/url]. Elle peut renvoyer des faux négatifs et ne tient pas compte d'autres travaux critiques pour la performance comme les animations et les mises à jour de rendu.

    scheduler.yield()

    Les limites de ces méthodes ont été une préoccupation pour l'équipe Chrome, d'autant plus que de nombreux sites échouent à l'[url=\/core-web-vitals\/interaction-to-next-paint]INP[\/url]. Pour remédier à cela, ils ont créé scheduler.yield() : une nouvelle API qui permet aux développeurs de rendre la main au thread principal immédiatement sans réorganiser l'ordre des tâches ni ajouter de complexité.

    scheduler.yield() a été [url=https:\/\/developer.chrome.com\/blog\/new-in-chrome-129]déployé en version stable dans Chrome 129[\/url] (septembre 2024) et est désormais supporté par tous les principaux navigateurs, à l'exception de Safari.

    Exemple de code

    Puisque Safari ne supporte pas encore scheduler.yield(), utilisez une solution de repli (fallback) avec setTimeout() :

    function yieldToMain() {
      if ('scheduler' in window && 'yield' in window.scheduler) {
        return window.scheduler.yield();
      }
      return new Promise((resolve) => {
        setTimeout(resolve, 0);
      });
    }
          

    La fonction yieldToMain() vérifie si window.scheduler.yield existe. Si c'est le cas, elle utilise l'API native. Sinon, le code se replie sur setTimeout(). Cela correspond au [url=https:\/\/web.dev\/articles\/optimize-long-tasks]modèle recommandé par Google[\/url].

    Pour les projets qui ont besoin de l'API Prioritized Task Scheduling complète (y compris scheduler.postTask() et TaskController), Google Chrome Labs maintient un [url=https:\/\/github.com\/nicjgriffin\/nicj-scheduler-polyfill]polyfill officiel[\/url].

    Exemple concret : améliorer la recherche avec yieldToMain()

    Voici comment yieldToMain() peut améliorer l'expérience de recherche pour vos utilisateurs.

    La fonction handleSearch() met d'abord à jour le contenu du bouton pour donner un retour immédiat qu'une recherche est en cours, puis rend la main pour permettre au navigateur d'afficher cette mise à jour. Ensuite, fetchData() récupère les données de recherche et updateHTML(data) affiche les résultats. Un autre yieldToMain() assure une mise à jour rapide de la mise en page. Enfin, les tâches moins importantes sont planifiées pendant le temps d'inactivité du navigateur. Notez que je n'ai pas rendu la main avant requestIdleCallback() puisqu'il ne s'exécute de toute façon que lorsque le thread principal est inactif.

    async function handleSearch() {
      \/* mettre à jour rapidement le contenu du bouton après l'envoi *\/
      updateButtonToPending();
    
      \/* Rendre la main au thread principal *\/
      await yieldToMain();
    
      \/* récupérer les données et mettre à jour le HTML *\/
      const data = await fetchData();
      updateHTML(data);
    
      \/* Rendre à nouveau la main au thread principal *\/
      await yieldToMain();
    
      \/* certaines fonctions ne devraient s'exécuter que pendant le temps d'inactivité du navigateur *\/
      requestIdleCallback(sendDataToAnalytics);
    }
          

    Pour un autre exemple pratique, découvrez comment [url=\/pagespeed\/datalayer-inp-yield-pattern]utiliser ce même modèle de yield avec les poussées dataLayer[\/url] pour empêcher les scripts d'analyse de bloquer les interactions.

    Pourquoi scheduler.yield() est meilleur

    Contrairement à setTimeout(), qui ajoute les tâches différées à la fin de la file d'attente des tâches, scheduler.yield() interrompt l'exécution et place la suite en tête de la file d'attente. Votre code reprend dès que les travaux de plus haute priorité (comme la gestion des rappels d'entrée) sont terminés. C'est la différence clé : vous pouvez rendre la main au thread principal sans risquer que d'autres scripts ne passent devant avant que votre code ne reprenne.

    scheduler.yield() est également conçu pour fonctionner avec l'API Prioritized Task Scheduling. Lorsqu'il est appelé à l'intérieur d'un rappel scheduler.postTask(), scheduler.yield() hérite du niveau de priorité de la tâche. Cette combinaison vous donne un contrôle précis sur la manière dont votre [url=\/pagespeed\/javascript-priority-levels]JavaScript est priorisé[\/url] et sur le moment où il rend la main.

    Support des navigateurs

    scheduler.yield() est supporté par [url=https:\/\/caniuse.com\/mdn-api_scheduler_yield]71,5 % des navigateurs dans le monde[\/url] :