Que pensez-vous qu'il se passera si vous exécutez ce morceau de code dans la console du navigateur?
function foo() { setTimeout(foo, 0); } foo();
Et celui-là?
function foo() { Promise.resolve().then(foo); } foo();
Si vous, comme moi, avez lu un tas d'articles sur la boucle d'événement, le thread principal, les tâches, les microtâches et plus, mais que vous avez du mal à répondre aux questions ci-dessus - cet article est pour vous.
Commençons donc. Le code de chaque page HTML du navigateur est exécuté dans
le thread principal . Le thread principal est le thread principal où le navigateur effectue JS, redessine, gère les actions des utilisateurs, et bien plus encore. C'est essentiellement là que le moteur JS est intégré au navigateur.
La façon la plus simple de le comprendre est de regarder le diagramme:
Figure 1Nous voyons que le seul endroit par lequel les tâches peuvent entrer dans la pile des appels et se terminer est la boucle d'événements. Imaginez que vous étiez à sa place. Et votre travail consiste à suivre le rythme des tâches. Les tâches peuvent être de deux types:
- Personnel - exécution du code JavaScript principal sur le site (ci-après nous supposerons qu'il a déjà été exécuté)
- Tâches client - Rendu, microtâches et tâches
Très probablement, vos tâches personnelles seront priorisées. Event Loop est d'accord avec cela :) Il reste à rationaliser les tâches du client.
Bien sûr, la première chose qui me vient à l'esprit est de donner à chaque client une priorité et de les aligner. La seconde consiste à déterminer exactement comment les tâches de chaque client seront traitées - une à la fois, en une seule fois ou peut-être par lots.
Jetez un œil à ce diagramme:
Figure 2Sur la base de ce schéma, l'ensemble du travail d'Event Loop est construit.
Après avoir commencé à exécuter un script, la tâche avec l'exécution de ce script est placée dans la file d'attente des tâches. Lorsque ce code est exécuté, nous rencontrons des tâches de différents clients, qui sont placées dans la file d'attente appropriée. Une fois la tâche d'exécution du script terminée (tâche à partir des tâches), la boucle d'événement va aux microtâches (après la tâche à partir des tâches, la boucle d'événement prend les tâches des microtâches). La boucle d'événement lui prend des tâches
jusqu'à leur fin . Cela signifie que si le temps de leur ajout est égal au temps de leur exécution, alors la boucle d'événement les ratissera sans fin.
Ensuite, il va à Render et effectue des tâches de lui. Les tâches de Render sont optimisées par le navigateur, et s'il considère qu'il n'est pas nécessaire de redessiner quoi que ce soit dans ce cycle, alors Event Loop ira simplement plus loin. Ensuite, Event Loop prend à nouveau les tâches des tâches et lui en demande une
seule, la première tâche de la file d'attente , la transmet à CallStack et va plus loin dans la boucle.
Si l'un des clients n'avait pas de tâches, la boucle d'événement passe simplement à la suivante. Et, au contraire, si les tâches du client prennent beaucoup de temps, les clients restants attendront leur tour. Et si les tâches de certains clients se sont révélées être sans fin, alors la pile d'appels déborde et le navigateur commence à jurer:
Figure 3 Maintenant que nous comprenons comment fonctionne la boucle d'événements, il est temps de comprendre ce qui se passe après l'exécution des extraits de code au début de cet article.
function foo() { setTimeout(foo, 0); } foo();
Nous voyons que la fonction foo s'appelle récursivement via setTimeout à l'intérieur, mais à chaque appel, elle crée une tâche client Tâches. Comme nous nous en souvenons, dans la boucle de boucle d'événements, lors de l'exécution de la file d'attente de tâches à partir de tâches, il ne prend qu'une seule tâche dans la boucle. Et puis il y a les tâches de Microtasks et Render. Par conséquent, ce morceau de code ne fera pas souffrir la boucle d'événements et ne ratifiera jamais ses tâches. Mais il lancera une nouvelle tâche pour les tâches client à chaque tour.
Essayons d'exécuter ce script dans le navigateur Google Chrome. Pour ce faire, j'ai créé un document HTML simple et connecté script.js avec ce morceau de code. Après avoir ouvert le document, allez dans les outils de développement et ouvrez l'onglet Perfomance et cliquez sur le bouton 'Démarrer le profilage et recharger la page':
Figure 4Nous voyons que les tâches des tâches sont effectuées une à la fois, environ une fois toutes les 4 ms.
Considérez le deuxième casse-tête:
function foo() { Promise.resolve().then(foo); } foo();
Ici, nous voyons la même chose que dans l'exemple ci-dessus, mais appeler foo ajoute des tâches à partir des microtâches, et elles sont toutes terminées jusqu'à leur fin. Cela signifie que tant que la boucle d'événement ne les aura pas terminés, il ne pourra pas passer au client suivant :( Et nous voyons à nouveau une triste
image .
Jetez un œil à cela dans les outils de développement:
Figure 5Nous constatons que les microtâches sont exécutées environ une fois toutes les 0,1 ms, ce qui est 40 fois plus rapide que la file d'attente des tâches. Tout cela parce qu'ils sont exécutés en une seule fois. Dans notre exemple, la file d'attente se déplace sans fin. Pour la visualisation, je l'ai réduit à 100 000 itérations.
C'est tout!
J'espère que cet article vous a été utile et que vous comprenez maintenant comment fonctionne la boucle d'événements et ce qui se passe dans les exemples de code ci-dessus.
Au revoir :) Et à bientôt. Si vous avez aimé, aimez et abonnez-vous à ma chaîne :)