Fermer

février 24, 2019

du téléchargement de scripts à l'exécution (première partie)


Cet article vous aidera à comprendre les éléments internes de JavaScript – même les parties les plus étranges. Chaque ligne de code que vous écrivez en JavaScript sera parfaitement logique une fois que vous saurez comment il a été interprété par le moteur sous-jacent. Vous apprendrez plusieurs manières de télécharger des scripts en fonction du cas d'utilisation et de la manière dont l'analyseur génère un arbre de syntaxe abstraite et ses méthodes heuristiques lors de l'analyse du code. Intéressons-nous de plus près aux composants internes des moteurs JavaScript – à commencer par le téléchargement de scripts.

Le langage JavaScript est l’un des langages les plus populaires. Fini l'époque où les gens utilisaient JavaScript uniquement pour gérer des écouteurs d'événements DOM et quelques tâches peu exigeantes. Aujourd'hui, vous pouvez créer une application complète à partir de la base en utilisant JavaScript. JavaScript a pris le dessus sur les vents, les terres et les mers. Avec Node.js envahissant la gamme des technologies côté serveur et l'avènement de bibliothèques riches et puissantes et de frameworks tels que React, Angular et Vue, JavaScript a conquis le Web. Les applications envoient beaucoup de JavaScript sur les câbles. Presque toutes les tâches compliquées d'une application sont maintenant implémentées à l'aide de JavaScript.

Bien que tout cela soit formidable, il est décourageant de constater que la plupart de ces applications sont dépourvues d'expérience utilisateur minimale. Nous continuons à ajouter des fonctionnalités à notre application sans prendre en compte ses implications en termes de performances. Il est important de suivre les techniques appropriées pour obtenir un code optimisé.

Dans cette série de didacticiels, nous allons tout d'abord comprendre ce qui ne va pas avec les techniques classiques, puis nous approfondirons l'analyse pour en apprendre davantage sur certaines Aidez-nous à écrire du code optimisé. Nous comprendrons également comment notre code est analysé, interprété et compilé par le moteur JavaScript sous-jacent et ce qui fonctionne le mieux pour nos moteurs. Bien que la syntaxe de JavaScript soit assez facile à comprendre, la compréhension de ses éléments internes est une tâche plus ardue. Nous allons commencer par les bases et finalement reprendre la bête.

Comprendre la balise de script

Considérons un fichier HTML simple:


 < html > 
     < head > 
         < script   src  =  ' ./ js / first.js '  >  </  script > [19659011] < script   src  =  ' ./ js / second.js '  >  </  script [19659008]> 
         < script   src  =  ' ./ js / third.js '  >  </  script > 
         < script   src  =  ' ./ js / third.js '  > >  ] </  script > 
     </  head > 
     < body > 
         < div > [19659000] ] Description de la balise script  </  div > 
     </  corps > 
 </  html > 
 

first.js inclut le code suivant:

 console .  log  ( "fichier first.js" ) 

second.js contient le code suivant :

 console .  log  ( "fichier second.js" ) 

J'ai mis en place un serveur express pour démontrer les concepts expliqués dans l'article. Si vous souhaitez expérimenter en cours de route, n'hésitez pas à cloner mon référentiel GitHub .

Voyons ce qui se passe lorsque nous ouvrons ce fichier HTML dans le navigateur:

 loading-scripts "title = "scripts de chargement" /><p data-recalc-dims= Le navigateur commence à analyser le code HTML. Lorsqu'il tombe sur une balise de script dans la section head, l'analyse HTML est suspendue. Une requête HTTP est envoyée au serveur pour récupérer le script. Le navigateur attend que tout le script soit téléchargé. Il effectue ensuite le travail d’analyse, d’interprétation et d’exécution du script téléchargé (nous entrerons dans les détails de l’ensemble du processus ultérieurement dans l’article). Cela se produit pour chacun des quatre scripts.

Une fois que cela est fait, le navigateur reprend son travail d'analyse HTML et de création de noeuds DOM. L’utilisateur, qui regarde patiemment l’écran en attente d’un chargement, ne sait pas qu’il passe le plus clair de son temps à exécuter du code JavaScript (même le code qui n’est pas forcément requis au démarrage). Les balises de script bloquent par nature. Ils bloquent le rendu du DOM. Votre professeur de lycée vous aurait peut-être dit: «Placez toujours les balises de script sous le corps». Maintenant que vous savez que les balises de script bloquent le rendu du DOM, il est logique de les placer sous le code HTML. Il vaut mieux afficher du contenu non interactif (pendant quelques millisecondes jusqu'à ce que le code JavaScript soit prêt) que rien du tout.

Imaginez que vous disposiez d'une très grande chaîne de nœuds DOM – des dizaines de milliers. D'après ce que nous avons appris jusqu'à présent, dans ce cas, l'utilisateur verrait beaucoup de contenu, mais il ne pourrait pas interagir, même avec l'élément le plus petit. Je suis sûr que vous avez visité des sites Web qui affichent l’ensemble du contenu presque instantanément mais ne vous permettent pas de faire défiler la liste ou même de cliquer sur un élément. La page ne semble pas bouger pendant quelques secondes. N’est-ce pas frustrant? La prochaine question évidente est la suivante: quand devrions-nous charger les scripts – au début avant l'analyse de HTML ou à la fin après le HTML? Analysons un peu plus le problème.

Notre objectif final est clair: charger les ressources instantanément lors du démarrage. Notre première approche consistant à analyser les scripts d’abord, puis le HTML, offre une bonne expérience utilisateur, mais elle consomme beaucoup de temps en lui montrant un écran vide pendant l’exécution du contenu. Le problème avec cette approche est qu’elle s’aggrave avec l’augmentation du nombre de scripts car le temps d’attente (temps de chargement) est directement proportionnel au nombre de scripts. Pour chaque script, nous allons au serveur et attendons qu'il soit téléchargé.

Pouvons-nous vider tout le code JavaScript dans un seul fichier? Cela réduirait le nombre de trajets effectués sur le serveur. Cela signifierait le dépôt de dizaines de milliers de lignes de JavaScript dans un fichier. Je ne vais certainement pas pour cela. Cela signifierait compromettre avec mon code d'éthique.

Entendu parler de Gulp, webpack? Ils ne sont rien d'autre que des bundleurs de modules en termes simples. Les groupeurs de modules, hein? Vous écrivez votre code JavaScript dans un nombre quelconque de fichiers (autant de modules que vous le souhaitez). Les groupeurs de modules regroupent tous vos fichiers JavaScript et vos ressources statiques dans un gros bloc, et vous pouvez simplement ajouter ce gros fichier dans votre code HTML.

Nous avons certainement réduit le nombre de requêtes HTTP adressées au serveur. Ne sommes-nous toujours pas en train de télécharger, analyser et exécuter tout le contenu? Pouvons-nous faire quelque chose à ce sujet? Il y a quelque chose appelé fractionnement de code. Avec webpack, vous pouvez diviser votre code en différents lots. Déposez tout le code commun dans un lot (comme Vendor.js, qui contient toutes les bibliothèques communes devant être utilisées dans le projet) et d’autres spécifiques aux modules.

Par exemple, supposons que vous construisez un site Web de commerce électronique. Vous avez différents modules pour le magasin, l'historique des transactions et le paiement. Cela n’a aucun sens de charger votre code spécifique au paiement sur la page spécifique au magasin. Les groupeurs ont résolu notre problème en faisant moins de requêtes HTTP au serveur.

Voyons maintenant un cas d'utilisation. J'ai ajouté Google Analytics pour mieux comprendre comment les utilisateurs interagissent avec mon site Web de commerce électronique. Le script Google Analytics n'est pas requis lors du démarrage. Nous voudrons peut-être charger les éléments spécifiques à l'application en premier, puis les autres scripts secondaires.

Téléchargement de scripts asynchrone

Lorsque vous ajoutez le mot clé async dans la balise de script, le navigateur télécharge ce script de manière asynchrone. Le navigateur ne met pas en pause l’analyse de DOM lorsqu'il rencontre une balise de script avec le mot clé async . Le script est téléchargé dans un autre thread sans perturber le thread principal et, une fois téléchargé, le navigateur met en pause l'analyse du code HTML et est occupé à analyser ce code de script. Une fois que l'analyse de ce code JavaScript est terminée, il est exécuté dans un autre thread et le navigateur reprend son travail d'analyse HTML. Nous avons économisé le temps d'attente du navigateur pendant le téléchargement du script.

Supposons que nous voulions télécharger deux de nos scripts de manière asynchrone:


 < html > 
     < head > 
         < script   asynchrone   src  =  ' ./ js / first.js '  

> </ script > < script async src = ' ./ js / seconde.js ' > </ script > < script src = ' ./ js / third .js ' > </ script > < script src = ' . /js/fourth.js'[19459004hootingde19659008]>[19659016unset</[19459004Scripts19269004_reverscript/19199099499179499179499179body> < div > [19659045] Description de la balise de script </ de > </ de corps > </ html >

Report de l’exécution des scripts

Lorsque vous ajoutez le mot clé defer dans la balise de script, le navigateur n’exécute ce script qu’après la fin de l’analyse HTML. Différer signifie simplement que l'exécution du fichier est différée ou différée. Le script est téléchargé dans un autre thread et est exécuté uniquement à la fin de l'analyse HTML.


 < html > 
     < head > 
         < script   defer   src  =  ' ./ js / first.js '  >  </  script > 
         < script   différer   src  =  ' ./ js / second.js '  >  </  script > 
         < script   src  =  ' ./ js / third.js '  

> [19659016] </ script > < script src = ' ./ js / quatrième.js ' 19659008]> </ script > </ head > < corps > < div [19659008]> Description de la balise de script </ div [19659008]> </ corps > </ html >

 defer-scripts "title =" defer-scripts "/></p data-recalc-dims=

Comme on peut le voir sur la capture d'écran ci-dessus, third.js et third.js ont été exécutés avant first.js et second.js. [19659003] Voici un bref aperçu des trois techniques d’ajout de scripts:

 Comparison "title =" Comparison "/></p data-recalc-dims=

Jusqu'à présent, nous avons compris comment les scripts étaient téléchargés et quels étaient les moyens les plus efficaces de les utiliser. les scripts de téléchargement sont. Voyons ce qui se passe après le téléchargement d’un script. (Nous envisageons d'utiliser le navigateur Chrome, bien que presque tous les navigateurs populaires suivent des étapes similaires.)

Chrome utilise la version 8 en tant que moteur JavaScript sous-jacent. Il comprend les composants suivants:

 js-engine "title =" js-engine "/></p data-recalc-dims=

  1. Parser – JavaScript est introduit dans un analyseur, qui génère un arbre de syntaxe abstraite
  2. . Interprète – L'arbre de syntaxe abstraite est l'entrée de l'interpréteur d'allumage V8, qui génère le compilateur ByteCode
  3. – Le compilateur Turbofan du moteur V8 prend le ByteCode et génère le code machine
  4. . Le compilateur optimiseur
  5. – Il faut entrer ByteCode et des données de profilage et générer un code machine optimisé

Nous détaillerons chacun de ces composants.

Analyse du code JavaScript

Le code source JavaScript est d'abord converti. Les jetons représentent l'alphabet d'une langue. Chaque unité du code source est identifiée par la grammaire de la langue que vous utilisez.

Donc, quelque chose comme var a = 1 est un déclaration JavaScript valide. Elle peut être décomposée en jeton. s (‘var’, ‘a’, ‘=’, ‘1’) qui correspondent à la grammaire linguistique. Cependant, quelque chose comme la variable a = 2 n’est pas une instruction JavaScript valide, car sa grammaire ne spécifie rien en rapport avec le mot clé variable . À l’aide de ces jetons, l’analyseur génère un arbre de syntaxe abstraite (AST) et des portées. AST, en termes simples, est une structure de données utilisée pour représenter le code source. Les portées sont également des structures de données, utilisées pour identifier la portée des variables dans leurs blocs définis. Par exemple, une variable locale serait accessible dans la portée locale et non dans la portée globale. Ces contraintes sont définies dans les structures de données de cette portée.

Considérez cet extrait de code JavaScript simple –

var a = 2

Je renvoie AST Explorer pour vérifier l'AST généré pour mon code. L'AST pour le code ci-dessus ressemblerait à ceci:

  {
   "type" :   "Programme" 
   "start" : [19659199] 0 
   "fin" :   9 
   "corps" :   [
     {
       "type" [19659008]:   "VariableDeclaration" 
       "start" :   0 
       "end" :   9 [1965999] 19659209] "déclarations" :   [
         {
           "type" :   "VariableDéclarator" 
           "start" :   4 
           "fin" :   9 
           "id" :   {
             "type" :   "Identifiant" 
             "début" :   4 
             "fin" :   5 
             "nom" [nom] [[19659008]:   "a" 
          } 
           "init" :   {
             "type" : [19659195] "Littéral" 
             "début" :   8 
             "fin" :   9 
             "valeur ":   2 
            " raw ":  " 2 "
          } 
        } 
      ] 
      " kind ":  " var "
    } 
  ] 
  " sourceType ":  " module "
} 

Essayons de créer sens de l'AST ci-dessus. C’est un objet JavaScript dont les propriétés sont de type début fin body et sourceType . start est l'index du premier caractère et end est la longueur de votre code, qui est var a = 2 dans ce cas. body contient la définition du code. C'est un tableau avec un seul objet puisqu'il n'y a qu'une seule instruction du type VariableDeclaration dans notre programme. À l'intérieur VariableDeclaration il spécifie l'identificateur a et sa valeur initiale est 2 . Vérifiez les objets id et init . Le type de déclaration est var . Cela peut également être let ou const .

Voyons un autre exemple pour mieux comprendre les AST:

  function   foo   ( )   {
     let  bar  =   2 
     retour  bar
} 

Et son AST est comme suit –

  {
   "type" :   "Programme" 
   "start"  :   0 
   "end" :   50 
  "body" :   [
     {
      type ":  " FonctionDéclaration "
      " début ":   0 
      " fin ":   50  , 
       "id" :   {
         "type" :   "identificateur" 
         "start" :   9 [19659008]
         "fin" :   12 
         "nom" :   "foo" 
      } 
       "expression" ":   false 
      " générateur ":   false 
      " params ":   []. 19659008]
       "body" :   {
         "type" :   "BlockStatement" 
         "start" [début]. 19659008]:   16 
         "end" :   50 
         "corps" :   [
           [
             "type" :   "VariableDeclaration" 
             "début" :   22 
             "fin" :   33 [19659008]
             "déclarations" :   [
	 {
                 "type" :   "VariableDeclarator" 
                 "début" . :   26 
                 "end" :   33 
                 "id" :   {
                   "type"  :   "Identificateur" 
                   "début" :   26 
                   "fin" :   29 [196594000] "nom" :   "bar" 
                } 
                 "init" :   {
                   "type" :   "Literal "
                  " début ":   32 
                  " fin ":   33 
                  " valeur "[19659008]:   2 
                   "raw" :   "2" 
                 "
	] :  " [genre19659008]:   "let" 
          } 
           {
             "type" :   "ReturnStatement" 
             "start"  :   38 
             "end" :   48 
             "argument" :   {
	 "type"  :   "Identificateur" 
	 "début" :   45 
	 "fin" :   48 
	 "nom" :   "bar" 
            } 
          } 
        ] 
      } 
    } 
  ] 
   "sourceType"  :   "module" 
} 

Encore une fois, il a des propriétés – de type début fin corps ] et sourceType . start vaut 0, ce qui signifie que le premier caractère est à la position 0, et end vaut 50, ce qui signifie que la longueur du code est 50. body est un tableau avec un objet du type FunctionDeclaration . Le nom de la fonction foo est spécifié dans l'objet id . Cette fonction ne prend aucun argument, donc params est un tableau vide. Le corps de la FunctionDeclaration est de type BlockStatement . BlockStatement identifie la portée de la fonction. Le corps du BlockStatement a deux objets pour VariableDeclaration et ReturnStatement . VariableDeclaration est identique à l'exemple précédent. ReturnStatement contient un argument portant le nom bar car bar est renvoyé par la fonction foo .

C'est ce qu'il est. C'est comment les AST sont générés. Quand j'ai entendu parler d'AST pour la première fois, j'ai pensé à eux comme de gros arbres effrayants aux nœuds compliqués. Mais maintenant que nous connaissons bien les AST, ne pensez-vous pas qu'il ne s'agit que d'un groupe de nœuds bien conçus représentant la sémantique d'un programme?

Parser prend également en charge Scopes.

 

 

 let  globalVar  =   2 
 function   foo   ()   {
     let  globalVar  (19659007) =   3 
    console .  log  ( 'globalVar'  globalVar ) 
} 

La fonction foo est destinée à l'impression 3 et non 2 car la valeur de globalVar dans son étendue est de 3. Lors de l'analyse syntaxique du code JavaScript, l'analyseur génère également les étendues correspondantes.

Lorsqu'un globalVar est référencé dans une fonction foo nous commençons par rechercher globalVar dans le périmètre fonctionnel. Si cette variable n'est pas trouvée dans l'étendue fonctionnelle, nous recherchons son parent, qui est dans ce cas l'objet global . Prenons un autre exemple:

  let  globalVar  =   2 
 function   foo   ()   {
     let  localVar  =   3 
    console .  log  ( 'localVar'  localVar ) 
    console .  log  ( 'globalVar'  globalVar ) 
} 
console .  log  ( 'localVar'  localVar ) 
console .  log  ( 'globalVar'  globalVar ) 

Les instructions de la console dans la fonction foo seraient imprimées 3 et 2 alors que les instructions de la console en dehors de la fonction foo afficheraient undefined et 3. En effet, localVar n'est pas accessible en dehors de la fonction foo . Il est défini dans le domaine d'application de la fonction foo et une recherche de localVar à l'extérieur de ce résultat donne undefined .

L'analyse syntaxique dans V8

V8 utilise deux analyseurs syntaxiques pour analyser le code JavaScript, appelés analyseur et pré-analyseur. Pour comprendre la nécessité de deux analyseurs, considérons le code ci-dessous:

  function   foo   ()   {
    console .  log  ( 'Je suis dans la fonction foo' ) 
} 

 function   bar   () [) 19659207] {
    console .  log  ( "Je suis dans la barre de fonctions" ) 
} 

 / * Fonction d’appel foo * / 
 foo  () 

Lorsque le code ci-dessus est analysé, l'analyseur génère un AST représentant la fonction foo et la fonction bar . Cependant, la fonction bar n'est appelée nulle part dans le programme. Nous passons du temps à analyser et à compiler des fonctions qui ne sont pas utilisées, du moins lors du démarrage. bar peut être appelé ultérieurement, par exemple en cliquant sur un bouton. Mais ce n'est clairement pas nécessaire lors du démarrage. Peut-on gagner ce temps en ne compilant pas la fonction bar lors du démarrage? Oui, nous le pouvons!

L’analyseur est ce que nous faisons jusqu’à présent. Il analyse tout votre code, construit des AST, étend des portées et trouve toutes les erreurs de syntaxe. Le pré-analyseur est comme un analyseur rapide. Il ne compile que ce qui est nécessaire et ignore les fonctions qui ne sont pas appelées. Il construit des portées mais ne construit pas d’AST. Il ne trouve qu'un ensemble limité d'erreurs et est environ deux fois plus rapide que l'analyseur. La V8 utilise une approche heuristique pour déterminer la technique d'analyse au moment de l'exécution.

Prenons un exemple pour comprendre comment la V8 analyse le code JavaScript:

  ( function   foo   ( )   {
    console .  log  ( "Je suis une fonction IIFE" ) 

     fonction   bar   ()   {
        console .  log  ( "Je suis une fonction interne de IIFE" ) 
    } 

 )  ( ) 

Lorsque l’analyseur trouve la parenthèse initiale, il comprend qu’il s’agit d’un IIFE et s’appelle immédiatement, il analyse donc la fonction foo en utilisant un analyseur complet ou un analyseur syntaxique enthousiaste. À l’intérieur foo lorsqu’il rencontre la fonction bar il analyse ou prépare paresseusement la fonction bar car, en se basant sur ses heuristiques, il sait que le La fonction bar ne sera pas appelée immédiatement. Lorsque la fonction foo est complètement analysée, V8 construit son AST ainsi que des oscilloscopes sans créer d’AST pour la fonction bar . Il ne construit que des portées pour la fonction bar .

Avez-vous déjà rencontré ce problème en écrivant le code JavaScript:

 analyseur-erreur "title =" analyseur-erreur "/></p data-recalc-dims=

Le code génère une erreur uniquement lorsque vous appelez la fonction fnClickListener car V8 n'analyse pas cette fonction lors du premier chargement. Il analyse la fonction fnClickListener uniquement lorsque vous l'appelez. .

Examinons quelques exemples supplémentaires pour mieux comprendre les heuristiques suivies par V8.

  function   toBeCalled  ()   [} 
 toBeCalled  () 

La fonction de BeCalled est analysée paresseusement par le moteur V8, qui utilise alors un analyseur complet pour le faire fonctionner. Le temps passé à analyser paresseusement la fonction toBeCalled est en réalité une perte de temps. La V8 analyse paresseusement la fonction à BeCalled elle ne sait pas que la déclaration immédiate serait un appel à cette fonction. Pour éviter cela, vous pouvez indiquer à V8 quelles fonctions doivent être analysées (analyse complète).

  ( fonction   àBeCalled   ()   {[19659008]} ) 
 àBeCalled  () 

Le fait d'envelopper une fonction entre parenthèses indique à V8 que cette fonction doit être analysée avec précaution. Vous pouvez également ajouter un point d'exclamation avant la déclaration de fonction pour indiquer à V8 d'analyser avec avidité cette fonction.

 !  function   de BeCalled   ()   {[19659008]} 
 àBeCalled  () 

Analyse des fonctions internes

 function   outer   ()   {
     function   inner [19659207] ()   {} 
} 

Dans ce cas, le V8 analyse paresseusement les deux fonctions, external et intérieure . Lorsque nous appelons outer la fonction outer est analysée avec minutie / minutieusement et la fonction inside est à nouveau analysée paresseusement. Cela signifie que la fonction intérieure est analysée deux fois paresseusement. La situation est encore pire lorsque les fonctions sont fortement imbriquées.

  function   outer   ()   {
     function   inner   () 19659207] {
         function   insideInner   ()   {} 
    } 
     retour  interne
} 

Initialement, les trois fonctions extérieures intérieures et à l'intérieur d'Inner sont paresseusement analysées.

  let  innerFn  =   outer  () 
 innerFn  () 

Lorsque nous appelons la fonction outer elle est entièrement analysée et Les fonctions intérieure et à l'intérieur intérieure sont analysées paresseusement. Maintenant, lorsque nous appelons intérieure intérieure est entièrement analysée et intérieure: Inner est analysée paresseusement. Cela fait que insideInner soit analysé trois fois. N'utilisez pas de fonctions imbriquées quand elles ne sont pas nécessaires. Utilisez correctement les fonctions imbriquées!

Analyse syntaxique des fermetures

 ( function   Extérieur   ()   {
     Laisser  de [19459354] a  =   2 
     let  b  =   3 
     fonction   intérieure   ()   {
         retour  a
    } 
     retour  intérieur
} ) 

Dans l'extrait de code ci-dessus, la fonction outer étant entourée de parenthèses, elle est analysée avec impatience. La fonction intérieure est analysée paresseusement. inner renvoie la variable a, qui entre dans le cadre de sa fonction outer . Il s'agit d'un cas valable de fermeture.

  let  innerFn  =   outer  () 
 innerFn  ()  innerFn  renvoie très bien une valeur de 2 puisqu'il a accès à la variable a de sa portée parente. Lors de l'analyse de la fonction  intérieure lorsque V8 rencontre la variable a, il recherche la variable a dans le contexte de la fonction  intérieure . Puisque a n'est pas présent dans le domaine de  inner il le vérifie dans le domaine de la fonction  outer . V8 comprend que la variable a doit être enregistrée dans le contexte de la fonction et doit être conservée même après l'exécution de la fonction  outer . Ainsi, la variable a est stockée dans le contexte de fonction de  outer  et est conservée jusqu'à l'exécution complète de sa fonction dépendante  inner . Veuillez noter que la variable b n'est pas conservée dans ce cas car elle n'est utilisée dans aucune des fonctions internes. 

Lorsque nous appelons la fonction innerFn la valeur de a n'est pas trouvée dans la pile d'appels, nous recherchons ensuite sa valeur dans le contexte de la fonction. Les recherches dans le contexte d'une fonction sont coûteuses comparées aux recherches dans la pile d'appels.

Vérifions le code analysé généré par V8.

  function   fnCalled   ()   {1965} 
    console.log('Inside fnCalled')
}

function fnNotCalled () {
    console.log('Inside fnNotCalled')
}

fnCalled()

As per our understanding, both of these functions will be lazily parsed and when we make a function call to fnCalledit would be fully parsed and print Inside fnCalled. Let’s see this in action. Run the file containing the above code as node --trace_parse parse.js. If you’ve cloned my GitHub repositoryyou’ll find this file under public/js folder. parse.js is the name of the file, and --trace_parse serves as an indicator to the runtime of nodejs to print the parsed output. This command would generate a dump of parsing logs. I’ll save the output of this command in a file parsedOutput.txt. For now, all that makes sense is the below screenshot of the dump.

parsed-output" title="parsed-output"/></p data-recalc-dims=

Function fnCalled is parsed, but function fnNotCalled is not parsed. Try searching for fnNotCalled in the dump.

Script Streaming

Now that we know how parsing works in V8, let’s understand one concept related to Script Streaming. Script Streaming is effective from Chrome version 41.

From what we’ve learned till now, we know it’s the main thread that parses the JavaScript code (even with async and defer keywords). With Script Streaming in place, now the parsing can happen in another thread. While the script is still getting downloaded by the main thread, the parser thread can start parsing the script. This means that the parsing would be completed in line with the download. This technique proves very helpful for large scripts and slow network connections. Check out the below image to understand how the browser operates with Script Streaming and without Script Streaming.

streaming" title="streaming"/></p data-recalc-dims=

In this tutorial, we learned multiple ways of downloading scripts based on the use case. We learned how the parser generates an Abstract Syntax Tree and its heuristics while parsing the code. Later in the article, we learned about Script Streaming. In the next article, we’ll learn how parsing code gets compiled by the V8 compiler.

For More on Building Apps with jQuery:

Want to learn more about creating great user interfaces with jQuery? Check out Kendo UI for jQuery - our complete UI component library that allows you to quickly build high-quality, responsive apps. It includes all the components you’ll need, from grids and charts to schedulers and dials.


Comments are disabled in preview mode.




Source link