Geekflare est soutenu par son public. Nous pouvons percevoir des commissions d'affiliation sur les liens d'achat présents sur ce site.
En Développement Dernière mise à jour : 25 septembre 2023
Partager sur :
Invicti Web Application Security Scanner - la seule solution qui offre une vérification automatique des vulnérabilités avec Proof-Based Scanning™.

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

Image – Hacker Noon

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

Source – https://youtu.be/8aGhZQkoFbQ

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 ?

Source – https://youtu.be/8aGhZQkoFbQ

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

Source – https://www.quora.com/How-does-an-event-loop-work/answer/Timothy-Maxwell

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.

  • Gbadebo Bello
    Auteur
Merci à nos sponsors
D'autres lectures intéressantes sur le développement
Alimentez votre entreprise
Quelques outils et services pour aider votre entreprise à se développer.
  • Invicti utilise le Proof-Based Scanning™ pour vérifier automatiquement les vulnérabilités identifiées et générer des résultats exploitables en quelques heures seulement.
    Essayez Invicti
  • Web scraping, proxy résidentiel, proxy manager, web unlocker, search engine crawler, et tout ce dont vous avez besoin pour collecter des données web.
    Essayez Brightdata
  • Monday.com est un système d'exploitation tout-en-un qui vous aide à gérer vos projets, vos tâches, votre travail, vos ventes, votre CRM, vos opérations, vos flux de travail et bien plus encore.
    Essayez le lundi
  • Intruder est un scanner de vulnérabilité en ligne qui détecte les faiblesses de votre infrastructure en matière de cybersécurité, afin d'éviter des violations de données coûteuses.
    Essayer l'intrus