Plongez en profondeur dans Javascript Internals
Fonctionnement interne du moteur Javascript, boucle d'événements, API Web, files d'attente d'événements et rappels.
Introduction
Avez-vous déjà imaginé ce qui se serait passé s'il n'y avait pas eu de Javascript ? En l'absence de Javascript, vous ne pourriez pas diffuser de films sur Netflix, acheter sur Amazon ou regarder des vidéos sur Youtube. Presque tous les autres sites Web utilisent Javascript et rendent la page Web interactive.
Avant l'invention de Javascript, Internet se composait simplement de pages HTML simples . HTML et CSS donnent de la structure et du style à une page Web. Mais Javascript rend la page Web dynamique . Vous pouvez entrer vos informations d'identification et vous connecter à un site Web, effectuer des transactions en ligne ou jouer à des jeux. Javascript rend tout possible.
L'une des enquêtes menées sur Stack-overflow a montré que Javascript a le plus grand nombre de dépôts sur GitHub. Chaque année, de nouveaux frameworks et bibliothèques sont développés en Javascript. La langue a véritablement transformé Internet et simplifié la vie des gens.
Arrière-plan
Il y a deux ans, je travaillais sur un projet parallèle et j'utilisais Javascript pour la première fois. Étant un programmeur chevronné depuis la dernière décennie, je me suis familiarisé avec la syntaxe et j'ai développé une bonne commande en trois mois.
Au fil des mois, la page Web que j'avais développée ne fonctionnait pas bien. Il a fallu beaucoup de temps pour charger et a commencé à avoir un impact sur les clients. J'ai donc fait une plongée en profondeur pour identifier la cause première.
Après avoir passé des nuits blanches, j'étais enfin à bout de souffle et sur le point d'abandonner. Cependant, j'ai décidé de faire un dernier essai. Enfin, j'ai pensé à bien comprendre le fonctionnement interne de Javascript. J'ai consulté de nombreuses ressources et j'ai finalement pu corriger le bogue dans mon code en deux jours.
Les ingénieurs se concentrent généralement sur la construction de nouvelles choses. Les outils et les frameworks que nous utilisons nous cachent l'essentiel de la complexité. Dans notre voyage, nous avons tendance à oublier comment les choses fonctionnent réellement dans les coulisses. Il est nécessaire de connaître le fonctionnement interne de tout outil que nous utilisons. Avoir une bonne compréhension des éléments internes nous aide à affiner le problème et à trouver une solution pragmatique.
Dans cet article, nous verrons ce qui se passe dans les coulisses lorsque Javascript est exécuté par le navigateur. Nous allons jeter un œil au moteur Javascript et comprendre son fonctionnement. Nous verrons les différents composants impliqués dans l'exécution de Javascript et obtiendrons des éclaircissements sur la boucle d'événements et la file d'attente d'événements. Bien que ces termes puissent être nouveaux pour vous, l'article tentera de simplifier chacun d'eux. Commençons par notre quête pour maîtriser les rouages de Javascript.
Javascript
Javascript est un langage interprété de haut niveau. Contrairement à C ou C++, nous n'avons pas besoin de compiler du code Javascript puis de l'exécuter. Le code Javascript est d'abord compilé en bytecode, puis traduit en code au niveau de la machine.
Les langages évolués tels que Java, C, C++, C# prennent en charge le multi-threading. Ces langages permettent aux programmeurs de créer plusieurs threads et d'exécuter les tâches dans différents threads. Ainsi, nous pouvons générer des tâches sur plusieurs processeurs et utiliser efficacement les ressources informatiques.
En revanche, Javascript est à thread unique. Cela signifie qu'il n'y a qu'un seul thread d'exécution à un moment donné. Si vous exécutez une fonction javascript prenant du temps, la page Web se figera. Vous avez peut-être constaté que le site Web ne répond pas plusieurs fois lors de la navigation.
Faisons une démonstration rapide et voyons le fonctionnement en action. J'ai ouvert Twitter et je peux cliquer sur les onglets individuels tels que Trending, News, Sportsetc.
Maintenant, dans ma developerconsole, j'écris une fonction qui contient une boucle infinie . Ci-dessous la définition de la fonction.
function infiniteLoop() {
for(i=0; i < 100000; ) {
}
}
infiniteLoop()
Le GIF ci-dessous montre que je ne parviens pas à changer l'onglet en Sportsou Entertaintment.
Puisque la fonction contient une boucle infinie, elle continuera à s'exécuter sans fin. Lorsque je sélectionne différents onglets, le navigateur est censé gérer l' clickévénement et afficher en conséquence le contenu de l'onglet cliqué. Je ne peux sélectionner aucun autre onglet car Javascript exécute déjà la fonction infiniteLoop.
Comme on le voit dans l'exemple ci-dessus, nous ne pouvons exécuter qu'une seule fonction à la fois en Javascript. Maintenant, vous vous demandez peut-être comment le navigateur est capable de charger des sites Web complexes en une fraction de seconde.
Nous comprendrons dans les sections suivantes comment Javascript fait cette magie dans les coulisses. Comprenons maintenant le cœur de Javascript, c'est-à-dire le moteur Javascript.
Moteur Javascript
Chaque navigateur fournit son propre moteur Javascript. Par exemple : - Chrome utilise le moteur V8, tandis que Firefox utilise le moteur SpiderMonkey. Le moteur est responsable de l'exécution du code Javascript ligne par ligne. Voici une liste des navigateurs populaires et de leurs moteurs Javascript.
Le moteur Javascript se compose d'un Heap et d'un Stack . Comprenons le fonctionnement de ces deux éléments.
Tas
Le tas est responsable du stockage des définitions de fonction, des variables d'objet. Il contient tous les objets référencés par le code.
Les objets sont créés dynamiquement dans le tas. Le fonctionnement est similaire à java. Chaque fois que l'objet est référencé par une fonction, il est extrait du tas.
function bar() {
var player = { "firstName": "Shubhman",
"lastName": "Gill"
}
var teams = ["India", "NewZealand"]
i =0
a = 10
console.log("In the function bar")
}
bar()
Objects on Heap
Empiler
La pile est une structure de données Last In First Out (LIFO). Chaque fois qu'une fonction est exécutée, les variables locales de la fonction sont poussées sur la pile. Une fois que la fonction a terminé son exécution, elle est supprimée de la pile. Si une fonction appelle une nouvelle fonction pendant l'exécution, la nouvelle fonction est également poussée sur la pile. Le moteur exécute la fonction qui se trouve au sommet de la pile.
Les fonctions qui sont au bas de la pile reprennent leur exécution une fois que toutes les fonctions au-dessus reviennent. Dans le cas où une fonction génère une erreur, une trace de pile est imprimée sur le fichier console. La trace de la pile consiste en une chaîne de fonctions qui ont été appelées lorsqu'une exception a été levée. Jetons un coup d'oeil à un exemple.
Je vais exécuter le code ci-dessus dans mon navigateur. Et ajoutez un point d'arrêt dans la fonction city.
Comme vu ci-dessus, lors de l'exécution du script, il s'est arrêté au point d'arrêt dans la fonction city. Sur le côté droit, dans la Call Stacksection, vous pouvez voir la cityfonction en haut.
Le Call Stackse compose de toutes les fonctions invoquées jusqu'à présent. Et la fonction la plus récente est placée en haut. Une fois que la fonction a terminé son exécution, elle est supprimée de la pile.
Vous pouvez voir dans l'image ci-dessous, la fonction cityest supprimée et l'exécution continue pour la fonction streetà la ligne numéro 9.
Comme vu ci-dessus, Javascript ne peut exécuter qu'une seule fonction à la fois et il n'y a qu'un seul thread d'exécution. Vous ne pouvez pas exécuter plusieurs fonctions simultanément dans la pile d'exécution.
Disons que nous avons une fonction qui effectue un HTTPappel et obtient des données d'un autre site Web. Que se passerait-il si le serveur était lent et que la fonction met longtemps à s'exécuter ? Nous ne pourrons exécuter aucune autre fonction et l'expérience utilisateur serait très mauvaise.
Est-ce que cela se produit dans le monde réel ? Non, des sites Web complexes comme Facebook.com , Twitter.com , Amazon.com , etc. exécutent des tonnes de code Javascript sans affecter l'expérience utilisateur. Pour comprendre comment, nous plongerons profondément dans le runtime Javascript.
Exécution Javascript
Javascript nous fournit diverses API pour récupérer des données sur le serveur, manipuler le DOM ou exécuter des fonctions à l'aide d'une minuterie. En raison de la nature à thread unique de Javascript, il peut arrêter le rendu d'un site Web, entraînant ainsi de mauvaises performances. Cependant, le runtime Javascript garantit que cela ne se produit pas. Comprenons cela avec un exemple.
Vous pouvez deviner quelle sera la sortie du code ci-dessous :-
Sera getSubTitlesimprimé après l'impression getActors? Et après avoir appelé setTimeout, le code s'arrêtera-t-il ?
Voici la sortie générée en exécutant le code ci-dessus : -
Comme vu ci-dessus, le getSubTitlesa été imprimé en premier et 4 secondes plus tard, getActorsa été imprimé. Bien que la fonction setTimeoutait attendu 4 secondes, elle a quand même exécuté la fonction suivante getSubTitlesavant et le code n'a pas attendu. Comprenons maintenant ce qui s'est passé dans les coulisses.
API Web
Vous ne trouverez pas l'implémentation de fonctions telles que setTimeout, ni fetchnulle part dans le code source Javascript. Ces fonctions sont fournies par le navigateur et ne font pas partie de Javascript. L'implémentation serait différente pour chaque navigateur.
Le navigateur fournit des fonctions pour effectuer des appels réseau, manipuler le DOM, traiter l'audio, les vidéos, etc. Ces fonctions forment les API Web. L'implémentation interne est complexe et se compose principalement de code C++. Les API nous fournissent une interface qui peut être invoquée à partir du code Javascript.
Lorsque vous appelez une fonction telle que setTimeout, le Javascript comprend qu'il s'agit d'une API Web. Il délègue ensuite l'exécution de cette fonction au conteneur de l'API Web. La fonction setTimeoutest exécutée dans un thread séparé. Il n'est pas exécuté dans le thread d'exécution du Javascript. C'est la raison pour laquelle le code écrit après setTimeoutest exécuté avant que la fonction de rappel ne lui soit transmise.
De même, comme les appels réseau prennent du temps, les fonctions telles que fetchsont exécutées dans un thread séparé. Les API Web du navigateur nous cachent la majeure partie de la complexité. Par exemple : - l'implémentation interne de fetchcontiendrait la formation d'une connexion TCP, l'envoi des données et leur analyse. De plus, il utiliserait également les ressources informatiques de manière efficace en planifiant l'exécution sur le thread approprié.
Le gif ci-dessous montre ce qui se passe lorsque vous exécutez des fonctions d'API Web telles que setTimeout.
Comme vu ci-dessus, une fois que la setTimeoutfonction est appelée, elle envoie la fonction de rappel getActorsaux API Web. Dans le conteneur des API Web, la fonction setTimeoutattend quatre secondes. En attendant, la fonction suivante getSubTitlesest appelée.
Maintenant, vous pouvez demander comment la fonction de rappel est getActorsexécutée après la fin de la minuterie. Nous verrons dans la section suivante, comment les rappels sont exécutés après le retour des API Web.
File d'attente d'événements
Javascript s'appuie fortement sur les fonctions de rappel. Les fonctions de rappel sont exécutées lorsqu'un événement se produit. Par exemple : - Après avoir reçu une réponse du serveur, nous devons imprimer la jsonreprésentation. Ou exécuter une fonction après un délai de 4 secondes.
L'environnement d'exécution Javascript consiste en un composant appelé file d'attente d'événements ou file d'attente de rappel. C'est une file d'attente de messages et utilise l'ordre premier entré, premier sorti (FIFO). Les messages ne sont rien d'autre que des fonctions de rappel programmées pour être exécutées.
Prenons un exemple pour comprendre cela. Dans le code ci-dessous, nous enregistrons une fonction de rappel chaque fois qu'il y a un événement de clic sur un bouton. Une fois que l'utilisateur clique sur le bouton, la fonction onClicksera appelée. Cette fonction appelle la fonction timeraprès un délai de 2 secondes.
L'animation ci-dessus montre ce qui se passe dans les coulisses. Nous avons ici deux API Web — a) $.onet b) setTimeout. La première API attend qu'un événement de clic ait lieu. Une fois qu'il reçoit un événement click, il exécutera la fonction onClick. La seconde API setTimeOutest ensuite appelée par la onClickfonction.
Chaque fois que l'utilisateur clique sur le bouton, la fonction onClickest poussée dans la file d'attente des événements. De même, après la fin de la minuterie, la fonction timerest poussée dans la file d'attente. Ainsi, la file d'événements est composée de toutes les fonctions de rappel qui doivent être exécutées. Dans ce cas, onClicket la timerfonction.
Boucle d'événement
Les fonctions de rappel EventQueueatteignent la pile d'exécution de Javascript via la boucle d'événement. La boucle d'événement vérifie en permanence si la pile Javascript actuelle est vide. S'il constate que la pile est vide, il sélectionne le premier message de la file d'attente d'événements et le pousse sur la pile.
En prenant l'exemple de la section précédente, dès que la boucle d'événements constate que la pile est vide, elle prend le message suivant de la file d'attente de rappel et l'exécute.
Vous trouverez ci-dessous un pseudo-code approximatif pour le fonctionnement de la boucle d'événements : -
Vous pouvez considérer une boucle d'événements comme une boucle infinie qui surveille en permanence la pile d'exécution. Si la pile est vide, elle récupère un message (fonction) dans la file d'attente d'événements et l'exécute.
Le diagramme ci-dessous montre la représentation de haut niveau du runtime Javascript.
Nous avons examiné les différents composants de l'environnement d'exécution Javascript. Vous aurez maintenant une meilleure idée du fonctionnement de Javascript sous le capot. Résumons ce que nous avons appris jusqu'à présent.
Résumé
- Javascript est monothread et exécute une fonction à la fois.
- Il exploite les API Web pour exécuter des tâches complexes en arrière-plan.
- Une fois l'exécution de l'API Web terminée, elle envoie les fonctions de rappel à la file d'attente d'événements.
- La boucle Event vérifie en permanence si la pile Javascript est vide. Si la pile est vide, il sélectionne la fonction de rappel dans la file d'attente des événements et l'exécute.
- Javascript interne et runtime
- Comment Javascript fonctionne dans le navigateur et le nœud
- Un guide animé de la boucle d'événements Node.js
- Qu'est-ce que c'est que la boucle d'événement de toute façon?
- Javascript asynchrone et boucle d'événements à partir de zéro
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































