Boucle d’événements JavaScript : le battement de cœur de la programmation asynchrone

JavaScript, bien qu’il s’agisse d’un langage monothread, gère plusieurs tâches simultanément grâce à sa puissante boucle d’événements. Que vous récupériez des données à partir d’une API, gériez les interactions des utilisateurs ou traitiez de grands ensembles de données, la boucle d’événements garantit que votre application reste réactive et efficace. Mais comment ce processus se déroule-t-il sous le capot ? Dans ce blog, nous découvrirons comment fonctionne la boucle d’événements JavaScript avec les opérations asynchrones. À la fin de cet article, vous aurez une compréhension claire du rôle de la boucle d’événements en JavaScript et de la manière dont vous pouvez l’exploiter pour écrire du code plus efficace et plus performant.
1. Les bases :
Pile d’appels: La pile d’appels est une structure de données LIFO (Last In, First Out) qui enregistre le point d’exécution actuel dans votre code. Chaque fois qu’une fonction est invoquée, elle est placée sur la pile et lorsqu’elle est terminée, elle est retirée de la pile.
Comment fonctionne la pile d’appels :
JavaScript est un langage synchrone à thread unique. Lorsqu’un programme JavaScript s’exécute, un contexte d’exécution global est créé et placé sur la pile d’appels.
function hello() {
console.log("hello there");
}
hello();
console.log("end")

Comment le programme est exécuté
Dans le contexte d’exécution global (GEC), l’intégralité du code est exécuté ligne par ligne. Tout d’abord, la définition de la fonction hello est rencontrée et de la mémoire lui est allouée. La ligne suivante est une invocation de fonction hello(). Lorsque cette fonction est invoquée, un nouveau contexte d’exécution est créé pour elle. Ce contexte pour la fonction « hello » est placé sur la pile d’appels.
Pendant que la fonction hello s’exécute, son code s’exécute ligne par ligne, affichant « bonjour » sur la console. Une fois l’exécution de la fonction hello terminée, son contexte d’exécution est retiré de la pile d’appels. Le contrôle passe ensuite à la ligne suivante du code, qui est console.log(« end »).
Après l’exécution de console.log(« end »), il ne reste plus de lignes de code à exécuter, donc le contexte d’exécution global est également retiré de la pile d’appels.
Sortir :
bonjour
fin

Diagramme de la pile d’appels et de l’API Web
API Web :
Dans l’environnement du navigateur, JavaScript a accès à diverses API Web, telles que setTimeout, les événements DOM et fetch. Lorsque ces API sont utilisées, elles gèrent les opérations de manière asynchrone. Ces API sont accessibles via l’objet window, bien que vous puissiez les utiliser directement sans référencer explicitement l’objet window. En interne, ils interagissent avec l’objet window.
Par exemple, lors de l’utilisation de setTimeout(), une minuterie est configurée. Une fois le minuteur terminé, il place la fonction de rappel dans la file d’attente de rappel. Nous explorerons le fonctionnement de ce processus plus en détail dans les exemples à venir.
Boucle d’événement :
La boucle d’événements est un processus continu qui surveille en permanence la pile d’appels. Si la pile d’appels est vide, elle traite d’abord les tâches de la file d’attente des microtâches (telles que les promesses et les observateurs de mutation) avant de passer aux tâches de la file d’attente des tâches (telles que les tâches setTimeout et I/O). La boucle d’événements garantit que les tâches sont exécutées dans le bon ordre et que l’application reste réactive.
Comment Event Loop entre en scène :
console.log("start")
setTimeout(function cb() {
console.log("Callback Executed");
}, 2000);
console.log("end")
Sortir:
Commencer
fin
Rappel exécuté
Le setTimeout La fonction enregistre une minuterie avec un retard de 2000 ms et enregistre également une fonction de rappel. Comme le minuteur s’exécute de manière asynchrone, JavaScript n’attend pas qu’il se termine et passe à l’exécution de la ligne de code suivante, en imprimant « end ». Une fois qu’il n’y a plus de tâches dans la pile d’appels, le contexte d’exécution global est extrait de la pile d’appels.
À ce stade, la fonction de rappel pour setTimeout est toujours en attente d’exécution. C’est là que la boucle événementielle entre en jeu. Lorsque le délai expire, la fonction de rappel est ajoutée à la file d’attente des tâches macro (ou tâches, ou rappel). La boucle d’événements vérifie en permanence la pile d’appels et, lorsqu’elle la trouve vide, pousse le rappel de la file d’attente des tâches de macro vers la pile d’appels pour exécution.
Vous vous demandez peut-être pourquoi la boucle d’événements est nécessaire et pourquoi nous ne pouvons pas directement pousser le rappel dans la pile d’appels. La boucle d’événements agit comme médiateur car il existe différents types de rappels enregistrés avec des priorités variables. En fonction de leur type, les rappels sont placés soit dans la file d’attente des tâches macro (rappel), soit dans la file d’attente des microtâches. La boucle d’événements sélectionne et traite ensuite ces rappels en fonction de leur priorité.
File d’attente de microtâches: Les microtâches (par exemple, les promesses résolues, les observateurs de mutations) ont une priorité plus élevée et sont traitées avant les tâches dans la file d’attente des tâches. Une fois chaque tâche de la pile d’appels terminée, la boucle d’événements traitera toutes les microtâches en attente jusqu’à ce que la file d’attente soit vide. Ce n’est qu’alors qu’il passera à la tâche suivante dans la file d’attente des tâches.
File d’attente des tâches (file d’attente de rappel): Les tâches (par exemple, setTimeout, setInterval, tâches d’E/S) ne sont traitées qu’une fois que la file d’attente des microtâches est vide. Cette file d’attente est également connue sous le nom de file d’attente de macrotâches.
console.log("start")
setTimeout(function cb() {
console.log("SetTimeout callback Executed");
}, 2000);
fetch('https://api.example.com/data') // Replace with valid url
.then(response => response.json())
.then(data => {
console.log('API Call Executed', data);
}).catch(error => {
console.error('Error:', error);
});
console.log("end")
Il existe deux opérations asynchrones : setTimeout et fetch.
Le setTimeout le rappel sera placé dans la file d’attente des tâches (ou file d’attente de rappel).
Le aller chercher call renvoie une promesse qui, une fois résolue, placera son rappel dans la file d’attente des microtâches.
Supposons que les deux files d’attente aient des tâches en attente en même temps. La boucle d’événements donnera la priorité et traitera en premier la file d’attente Microtask. Une fois que la file d’attente des microtâches est vide, elle traitera les tâches dans la file d’attente des tâches.

Flux de traitement des boucles d’événements et des files d’attente
Sortir: Dépend de la réponse de l’API (en supposant que la réponse de l’API arrive en moins de 2 secondes)
commencer
fin
Appel API exécuté { … } // (ou un message d’erreur)
Rappel SetTimeout Exécuté
Conclusion
Comprendre la boucle d’événements JavaScript est essentiel pour maîtriser la programmation asynchrone. La boucle d’événements est au cœur du modèle de concurrence JavaScript, permettant des opérations d’E/S non bloquantes et garantissant que même les applications complexes restent réactives. En faisant la distinction entre la pile d’appels, la file d’attente de microtâches et la file d’attente de rappel et en comprenant comment elles interagissent, les développeurs peuvent écrire un code plus efficace, plus prévisible et sans bug. Au fur et à mesure que vous en apprendrez davantage sur JavaScript, comprendre le fonctionnement de la boucle d’événements vous aidera à rendre vos applications plus rapides et plus conviviales.
Source link