Alors qu’une connaissance approfondie de langages tels que le C et le C peut être nécessaire pour écrire un code de production à grande échelle, JavaScript peut souvent être écrit avec seulement une connaissance de base de ce qui peut être fait avec le langage
Des concepts tels que le passage de callbacks à des fonctions ou l’écriture de code asynchrone ne sont souvent pas si difficiles à mettre en œuvre, ce qui fait que la plupart des développeurs JavaScript se soucient peu de ce qui se passe sous le capot. Ils ne se soucient tout simplement pas de comprendre les complexités dont le langage les a profondément abstraits
En tant que développeur JavaScript, il devient de plus en plus important de comprendre ce qui se passe réellement sous le capot et comment la plupart de ces complexités abstraites fonctionnent réellement. Cela nous aide à prendre des décisions plus éclairées, ce qui peut, à son tour, améliorer considérablement les performances de notre code
Cet article se concentre sur l’un des concepts ou termes les plus importants mais rarement compris en JavaScript. La boucle d’événement (EVENT LOOP).
L’écriture de code asynchrone ne peut être évitée en JavaScript, mais que signifie réellement un code fonctionnant de manière asynchrone ? i.e. La boucle événementielle
Avant de comprendre comment fonctionne la boucle d’événements, nous devons d’abord comprendre ce qu’est JavaScript et comment il fonctionne !
Qu’est-ce que JavaScript ?
Avant de poursuivre, j’aimerais que nous fassions un pas en arrière pour revenir à l’essentiel. Qu’est-ce que JavaScript ? Nous pourrions définir JavaScript comme suit
JavaScript est un langage interprété de haut niveau, à un seul fil d’exécution, non bloquant, asynchrone et concurrent
Attendez, qu’est-ce que c’est ? Une définition livresque ? 🤔
Décomposons-la !
En ce qui concerne cet article, les mots clés sont : monotâche, non bloquant, concurrent et asynchrone
Monotâche
Un thread d’exécution est la plus petite séquence d’instructions programmées qui peut être gérée indépendamment par un planificateur. Un langage de programmation est monofilaire lorsqu’il ne peut exécuter qu’une seule tâche ou opération à la fois. Cela signifie qu’il peut exécuter un processus complet du début à la fin sans que le fil d’exécution ne soit interrompu ou arrêté
Contrairement aux langages multithreads où plusieurs processus peuvent être exécutés simultanément sur plusieurs threads sans se bloquer les uns les autres
Comment JavaScript peut-il être à la fois monofilaire et non bloquant?
Mais qu’entend-on par blocage ?
Non-bloquant
Il n’existe pas de définition unique du blocage ; il s’agit simplement de choses qui s’exécutent lentement sur le thread. Donc, non bloquant signifie des choses qui ne sont pas lentes sur le thread
Mais attendez, ai-je dit que JavaScript fonctionnait sur un seul thread ? Et j’ai également dit qu’il était non bloquant, ce qui signifie que les tâches s’exécutent rapidement sur la pile d’appels ? Mais comment ? Qu’en est-il lorsque nous exécutons des chronomètres ? Des boucles ?
Détendez-vous ! Nous le découvrirons dans un instant 😉
Concurrent
Concurrence signifie que le code est exécuté simultanément par plus d’un thread
Ok, les choses deviennent vraiment bizarres maintenant, comment JavaScript peut-il être mono-thread et être concurrent en même temps ? c’est-à-dire, exécuter son code avec plus d’un thread ?
Asynchrone
La programmation asynchrone signifie que le code s’exécute dans une boucle d’événements. Lorsqu’il y a une opération bloquante, l’événement est lancé. Le code bloquant continue à s’exécuter sans bloquer le fil d’exécution principal. Lorsque le code bloquant termine son exécution, il met en file d’attente le résultat des opérations bloquantes et les repousse sur la pile
Mais JavaScript n’a qu’un seul fil d’exécution ? Qu’est-ce qui exécute alors ce code bloquant tout en laissant les autres codes du fil d’exécution s’exécuter ?
Avant de poursuivre, récapitulons ce qui précède
- JavaScript est un thread unique
- JavaScript est non bloquant, c’est-à-dire que les processus lents ne bloquent pas son exécution
- JavaScript est concurrent, c’est-à-dire qu’il exécute son code dans plusieurs threads en même temps
- JavaScript est asynchrone, c’est-à-dire qu’il exécute le code bloquant ailleurs.
Mais ce qui précède ne correspond pas exactement à la réalité : comment un langage à un seul thread peut-il être non bloquant, concurrent et asynchrone ?
Allons un peu plus loin, descendons jusqu’au moteur d’exécution JavaScript, le V8, il a peut-être des threads cachés dont nous ne sommes pas conscients
Le moteur V8
Le moteur V8 est un moteur d’exécution d’assemblages web à haute performance et à source ouverte pour JavaScript, écrit en C par Google. La plupart des navigateurs exécutent JavaScript à l’aide du moteur V8, et même le populaire environnement d’exécution node js l’utilise également
En termes simples, le V8 est un programme C qui reçoit le code JavaScript, le compile et l’exécute
Le V8 effectue deux tâches principales
- L’allocation de la mémoire du tas
- Contexte d’exécution de la pile d’appels
Malheureusement, nos soupçons étaient erronés. Le V8 n’a qu’une seule pile d’appels, considérez la pile d’appels comme le fil d’exécution
Un thread === une pile d’appels === une exécution à la fois

Puisque V8 n’a qu’une seule pile d’appels, comment JavaScript peut-il s’exécuter de manière concurrente et asynchrone sans bloquer le fil d’exécution principal ?
Essayons de le découvrir en écrivant un code asynchrone simple mais courant et analysons-le ensemble
JavaScript exécute chaque code ligne par ligne, l’un après l’autre (single-threaded). Comme prévu, la première ligne est imprimée dans la console, mais pourquoi la dernière ligne est-elle imprimée avant le code timeout ? Pourquoi le processus d’exécution n’attend-il pas le code timeout (blocage) avant d’exécuter la dernière ligne ?
Un autre thread semble nous avoir aidés à exécuter ce timeout, car nous sommes pratiquement certains qu’un thread ne peut exécuter qu’une seule tâche à un moment donné
Jetons un coup d’œil dans le code source de V8
Attendez… quoi ? !!! Il n’y a pas de fonctions de temporisation dans V8, pas de DOM ? Pas d’événements ? Pas d’AJAX ?…. Oui, oui !!!
Les événements, DOM, les minuteries, etc. ne font pas partie de l’implémentation de base de JavaScript. JavaScript se conforme strictement aux spécifications Ecma Scripts et ses différentes versions sont souvent désignées par les spécifications Ecma Scripts (ES X)
Flux d’exécution
Les événements, les minuteries et les requêtes Ajax sont tous fournis côté client par les navigateurs et sont souvent appelés API Web. Ce sont eux qui permettent au JavaScript à fil unique d’être non bloquant, concurrent et asynchrone ! Mais comment ?
Le flux d’exécution de tout programme JavaScript comporte trois sections principales : la pile d’appels, l’API Web et la file d’attente des tâches
La pile d’appels
Une pile est une structure de données dans laquelle le dernier élément ajouté est toujours le premier à être retiré de la pile. Vous pouvez l’assimiler à une pile d’assiettes dans laquelle seule la première assiette qui a été ajoutée en dernier peut être retirée en premier. Une pile d’appels n’est rien d’autre qu’une structure de données où les tâches ou le code sont exécutés en conséquence
Prenons l’exemple suivant

Lorsque vous appelez la fonction printSquare
() , elle est poussée sur la pile d’appels, la fonction printSquare()
appelle la fonction square(). La fonction square(
) est poussée sur la pile et appelle également la fonction multiply()
. La fonction multiplier est poussée sur la pile. Étant donné que la fonction multiply revient et qu’elle est la dernière chose qui a été poussée sur la pile, son get est résolu en premier et est retiré de la pile, suivi de la fonction square()
et de la fonction printSquare()
L’API Web
C’est ici que le code qui n’est pas géré par le moteur V8 est exécuté pour ne pas “bloquer” le fil d’exécution principal. Lorsque la pile d’appels rencontre une fonction de l’API Web, le processus est immédiatement transféré à l’API Web, où il est exécuté et libère la pile d’appels pour qu’elle puisse effectuer d’autres opérations pendant son exécution
Revenons à notre exemple setTimeout
ci-dessus
Lorsque nous exécutons le code, la première ligne de console.log est poussée vers la pile et nous obtenons notre sortie presque immédiatement, lorsque nous arrivons au délai d’attente, les délais sont gérés par le navigateur et ne font pas partie de l’implémentation de base de V8, il est poussé vers l’API Web à la place, libérant la pile pour qu’elle puisse effectuer d’autres opérations
Pendant que le délai d’attente est toujours en cours, la pile passe à la ligne d’action suivante et exécute le dernier console.log, ce qui explique pourquoi nous obtenons cela avant la sortie du minuteur. Une fois que le timer est terminé, quelque chose se passe. Le console.log de la minuterie réapparaît comme par magie dans la pile d’appels !
Comment ?
La boucle d’événements
Avant d’aborder la boucle d’événements, examinons d’abord la fonction de la file d’attente des tâches
Revenons à notre exemple de délai d’attente. Une fois que l’API Web a terminé l’exécution de la tâche, elle ne la repousse pas automatiquement dans la pile d’appels. Elle va dans la file d’attente des tâches.
Une file d’attente est une structure de données qui fonctionne selon le principe du premier entré, premier sorti, c’est-à-dire que les tâches sont introduites dans la file d’attente et en sortent dans le même ordre. Les tâches qui ont été exécutées par les API Web et qui sont placées dans la file d’attente des tâches retournent ensuite dans la pile d’appels pour que leur résultat soit imprimé
Mais attendez. QU’EST-CE QUE LA BOUCLE D’ÉVÉNEMENTS ?

La boucle d’événements est un processus qui attend que la pile d’appels soit vide avant de pousser les rappels de la file d’attente des tâches vers la pile d’appels. Une fois que la pile est vide, la boucle d’événements se déclenche et vérifie si des rappels sont disponibles dans la file d’attente des tâches. S’il y en a, elle les place dans la pile d’appels, attend que la pile d’appels soit à nouveau vide et répète le même processus

Le diagramme ci-dessus illustre le flux de travail de base entre la boucle d’événements et la file d’attente des tâches
Conclusion
Bien qu’il s’agisse d’une introduction très basique, le concept de programmation asynchrone en JavaScript donne un aperçu suffisant pour comprendre clairement ce qui se passe sous le capot et comment JavaScript est capable de fonctionner de manière concurrente et asynchrone avec un seul thread
JavaScript est toujours à la demande, et si vous êtes curieux d’apprendre, je vous conseille de jeter un coup d’œil à ce cours Udemy.