Approfondimento su Javascript Internals

Feb 02 2023
Funzionamento interno del motore Javascript, ciclo di eventi, API Web, code di eventi e richiamate. Introduzione Hai mai immaginato cosa sarebbe successo se non ci fosse Javascript? In assenza di Javascript, non saresti in grado di riprodurre film in streaming su Netflix, fare acquisti su Amazon o guardare video su Youtube.

Funzionamento interno del motore Javascript, ciclo di eventi, API Web, code di eventi e richiamate.

JavaScript

introduzione

Hai mai immaginato cosa sarebbe successo se non ci fosse Javascript? In assenza di Javascript, non saresti in grado di riprodurre film in streaming su Netflix, fare acquisti su Amazon o guardare video su Youtube. Quasi tutti gli altri siti Web utilizzano Javascript e rendono la pagina Web interattiva.

Prima che Javascript fosse inventato, Internet consisteva solo di semplici pagine HTML . HTML e CSS danno struttura e stile a una pagina web. Ma Javascript rende dinamica la pagina web . Puoi inserire le tue credenziali e accedere a un sito Web, eseguire transazioni online o giocare. Javascript fa accadere tutto.

Uno dei sondaggi condotti su Stack-overflow ha mostrato che Javascript ha il maggior numero di repository su GitHub. Ogni anno vengono sviluppati nuovi framework e librerie in Javascript. La lingua ha davvero trasformato Internet e semplificato la vita delle persone.

Sfondo

Due anni fa, stavo lavorando a un progetto parallelo e ho usato Javascript per la prima volta. Essendo un programmatore esperto dall'ultimo decennio, sono diventato esperto di sintassi e ho sviluppato una buona padronanza in tre mesi.

Con il passare dei mesi, la pagina web che avevo sviluppato non funzionava bene. Ci è voluto molto tempo per caricarsi e ha iniziato ad avere un impatto sui clienti. Quindi, ho fatto un'immersione profonda per identificare la causa principale.

Dopo aver passato notti insonni, ero finalmente al limite e stavo per arrendermi. Tuttavia, ho deciso di fare un ultimo tentativo. Infine, ho pensato di ottenere una comprensione approfondita di come Javascript funziona internamente. Ho fatto riferimento a molte risorse e sono stato finalmente in grado di correggere il bug nel mio codice entro due giorni.

Gli ingegneri sono generalmente concentrati sulla costruzione di cose nuove. Gli strumenti e le strutture che utilizziamo ci nascondono la maggior parte della complessità. Nel nostro viaggio, tendiamo a dimenticare come funzionano effettivamente le cose dietro le quinte. È necessario conoscere il funzionamento interno di qualsiasi strumento che utilizziamo. Avere una buona comprensione degli interni ci aiuta a restringere il problema e trovare una soluzione pragmatica.

In questo articolo, vedremo cosa succede dietro le quinte quando Javascript viene eseguito dal browser. Daremo un'occhiata al motore Javascript e capiremo che funziona. Vedremo i diversi componenti coinvolti nel runtime di Javascript e faremo chiarezza sul ciclo degli eventi e sulla coda degli eventi. Anche se questi termini potrebbero essere nuovi per te, ma l'articolo cercherà di semplificare ciascuno di essi. Cominciamo con la nostra ricerca per padroneggiare gli interni di Javascript.

JavaScript

Javascript è un linguaggio interpretato di alto livello. A differenza di C o C++, non dobbiamo compilare codice Javascript e quindi eseguirlo. Il codice Javascript viene prima compilato in bytecode e poi tradotto in codice a livello di macchina.

I linguaggi avanzati come Java, C, C++, C# supportano il multithreading. Questi linguaggi consentono ai programmatori di creare più thread ed eseguire le attività in thread diversi. Pertanto, possiamo generare attività su più CPU e utilizzare in modo efficiente le risorse di calcolo.

Al contrario, Javascript è a thread singolo. Ciò significa che esiste un solo thread di esecuzione in un dato momento. Nel caso in cui esegui una funzione javascript che richiede tempo, la pagina web si bloccherà. Potresti aver visto che il sito Web non risponde molte volte durante la navigazione.

Errore di mancata risposta della pagina

Facciamo una rapida demo e vediamo il funzionamento in azione. Ho aperto Twitter e posso fare clic sulle singole schede come Trending, News, Sportsecc.

Esplora la sezione di Twitter

Ora, nella mia developerconsole, sto scrivendo una funzione che contiene un ciclo infinito . Di seguito la definizione della funzione.

function infiniteLoop() {
    for(i=0; i < 100000; ) {
    }
}
infiniteLoop()

Sotto GIF mostra che non sono in grado di cambiare la scheda in Sportso Entertaintment.

Schermo bloccato a causa di loop infinito

Poiché la funzione contiene un ciclo infinito, continuerà a essere eseguita all'infinito. Quando seleziono diverse schede, il browser dovrebbe gestire l' clickevento e di conseguenza visualizzare il contenuto della scheda cliccata. Non riesco a selezionare nessun'altra scheda poiché Javascript sta già eseguendo la funzione infiniteLoop.

Come visto nell'esempio sopra, possiamo eseguire solo una funzione alla volta in Javascript. Ora, potresti chiederti in che modo il browser è in grado di caricare siti Web complessi in una frazione di secondo.

Capiremo nelle sezioni successive come Javascript fa questa magia dietro le quinte. Vediamo ora di capire il nucleo di Javascript ovvero Javascript Engine.

Motore Javascript

Ogni browser fornisce il proprio motore Javascript. Ad esempio: - Chrome utilizza il motore V8, mentre Firefox utilizza SpiderMonkey Engine. Il motore è responsabile dell'esecuzione del codice Javascript riga per riga. Di seguito è riportato un elenco dei browser più diffusi e dei relativi motori Javascript.

Il motore Javascript è costituito da un Heap e uno Stack . Cerchiamo di capire il funzionamento di questi due elementi.

Mucchio

L' heap è responsabile della memorizzazione delle definizioni delle funzioni, delle variabili oggetto. Contiene tutti gli oggetti a cui fa riferimento il codice.

Gli oggetti vengono creati dinamicamente nell'heap. Il funzionamento è simile a java. Ogni volta che l'oggetto viene referenziato da una funzione, viene recuperato dall'heap.

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

Pila

Lo Stack è una struttura dati LIFO (Last In First Out). Ogni volta che viene eseguita una funzione, le variabili locali della funzione vengono inserite nello stack. Una volta completata l'esecuzione, la funzione viene rimossa dallo stack. Se una funzione chiama una nuova funzione durante l'esecuzione, anche la nuova funzione viene inserita nello stack. Il motore esegue la funzione che si trova in cima allo stack.

Le funzioni che si trovano in fondo allo stack riprendono la loro esecuzione una volta che tutte le funzioni sopra di esso ritornano. Nel caso in cui una funzione generi un errore, viene stampata una traccia dello stack sul file console. La traccia dello stack è costituita da una catena di funzioni che sono state chiamate quando è stata generata un'eccezione. Diamo un'occhiata a un esempio.

Ho intenzione di eseguire il codice sopra nel mio browser. E aggiungi un punto di interruzione nella funzione city.

Esecuzione Javascript nel browser

Come visto sopra, durante l'esecuzione dello script, si è fermato al punto di interruzione nella funzione city. Sul lato destro, nella Call Stacksezione, puoi vedere la cityfunzione in alto.

Il Call Stackconsiste di tutte le funzioni invocate finora. E la funzione più recente è posizionata in alto. Una volta completata l'esecuzione, la funzione viene rimossa dallo stack.

Puoi vedere nell'immagine qui sotto, la funzione cityviene rimossa e l'esecuzione continua per la funzione streetalla riga numero 9.

Come visto sopra, Javascript può eseguire solo una funzione alla volta e c'è solo un thread di esecuzione. Non è possibile eseguire più funzioni contemporaneamente nello stack di esecuzione.

Supponiamo di avere una funzione che esegue una HTTPchiamata e ottiene dati da un altro sito web. Cosa accadrebbe se il server è lento e la funzione impiega molto tempo per essere eseguita? Non saremo in grado di eseguire nessun'altra funzione e l'esperienza dell'utente sarebbe molto scadente.

Questo accade nel mondo reale? No, siti Web complessi come Facebook.com , Twitter.com , Amazon.com , ecc. Eseguono tonnellate di codice Javascript senza influire sull'esperienza dell'utente. Per capire come, approfondiremo il runtime di Javascript.

Tempo di esecuzione Javascript

Javascript ci fornisce varie API per recuperare dati dal server, manipolare il DOM o eseguire funzioni utilizzando un timer. A causa della natura a thread singolo di Javascript, può arrestare il rendering di un sito Web, portando così a prestazioni scadenti. Tuttavia, il runtime Javascript garantisce che ciò non accada. Capiamolo con un esempio.

Puoi indovinare quale sarà l'output del codice seguente: -

Verrà getSubTitlesstampato dopo la stampa getActors? E dopo aver chiamato setTimeout, il codice si fermerà?

Di seguito è riportato l'output generato eseguendo il codice precedente: -

Uscita codice

Come visto sopra, è getSubTitlesstato stampato prima e 4 secondi dopo, getActorsè stato stampato. Sebbene la funzione setTimeoutabbia aspettato per 4 secondi, ha comunque eseguito la funzione successiva getSubTitlesprima e il codice non ha aspettato. Vediamo ora cosa è successo dietro le quinte.

API web

Non troverai l'implementazione di funzioni come setTimeout, o fetchda nessuna parte nel codice sorgente Javascript. Queste funzioni sono fornite dal browser e non fanno parte di Javascript. L'implementazione sarebbe diversa per ogni browser.

Il browser fornisce funzioni per eseguire chiamate di rete, manipolare il DOM, elaborare audio, video, ecc. Queste funzioni formano le Web API. L'implementazione interna è complessa e consiste principalmente di codice C++. Le API ci forniscono un'interfaccia che può essere richiamata dal codice Javascript.

Quando invochi una funzione come setTimeout, Javascript capisce che si tratta di un'API Web. Quindi delega l'esecuzione di questa funzione al contenitore dell'API Web. La funzione setTimeoutviene eseguita in un thread separato. Non viene eseguito nel thread di esecuzione di Javascript. Questo è il motivo per cui il codice scritto dopo setTimeoutviene eseguito prima che la funzione di callback gli venga passata.

Allo stesso modo, poiché le chiamate di rete richiedono tempo, le funzioni come fetchvengono eseguite in un thread separato. Le API Web del browser ci nascondono la maggior parte della complessità. Ad esempio: - l'implementazione interna di fetchconterrebbe la formazione di una connessione TCP, l'invio dei dati e l'analisi. Inoltre, utilizzerebbe anche le risorse di calcolo in modo efficiente pianificando l'esecuzione sul thread appropriato.

La gif seguente mostra cosa succede quando esegui funzioni API Web come setTimeout.

Javascript in azione

Come visto sopra, una volta che la setTimeoutfunzione viene chiamata, invia la funzione di callback getActorsalle Web API. Nel contenitore delle API Web, la funzione setTimeoutattende quattro secondi. Nel frattempo, getSubTitlesviene chiamata la funzione successiva.

Ora, potresti chiedere come viene getActorseseguita la funzione di callback al termine del timer. Vedremo nella sezione successiva come vengono eseguiti i callback dopo il ritorno delle API Web.

Coda eventi

Javascript fa molto affidamento sulle funzioni di callback. Le funzioni di callback vengono eseguite quando si verifica un evento. Ad esempio:- Dopo aver ricevuto una risposta dal server, dobbiamo stampare la jsonrappresentazione. Oppure eseguire una funzione dopo un ritardo di 4 secondi.

Il runtime Javascript è costituito da un componente noto come Event Queue o Callback Queue. È una coda di messaggi e utilizza l'ordine FIFO (First In First Out). I messaggi non sono altro che funzioni di callback programmate per essere eseguite.

Facciamo un esempio per capirlo. Nel codice seguente, stiamo registrando una funzione di callback ogni volta che c'è un evento click su un pulsante. Una volta che l'utente fa clic sul pulsante, la funzione onClickverrà chiamata. Questa funzione chiama la funzione timerdopo un ritardo di 2 secondi.

L'animazione sopra mostra cosa succede dietro le quinte. Abbiamo due API Web qui: a) $.one b) setTimeout. La prima API attende che si verifichi un evento clic. Una volta ricevuto un evento click, eseguirà la funzione onClick. La seconda API setTimeOutviene chiamata successivamente dalla onClickfunzione.

Ogni volta che l'utente fa clic sul pulsante, la funzione onClickviene inserita nella coda degli eventi. Allo stesso modo, al termine del timer, la funzione timerviene inserita nella coda. Quindi la coda degli eventi è composta da tutte le funzioni di callback che devono essere eseguite. In questo caso, onClicke la timerfunzione.

Ciclo di eventi

Le funzioni di callback EventQueueraggiungono lo stack di esecuzione di Javascript tramite Event Loop. Il ciclo Event controlla continuamente se lo stack Javascript corrente è vuoto. Se rileva che lo stack è vuoto, preleva il primo messaggio dalla coda degli eventi e lo inserisce nello stack.

Prendendo l'esempio della sezione precedente, non appena Event Loop rileva che lo stack è vuoto, prenderà il messaggio successivo dalla coda di callback e lo eseguirà.

Di seguito è riportato uno pseudo codice approssimativo per il funzionamento del ciclo di eventi: -

Puoi pensare a un ciclo di eventi come a un ciclo infinito che monitora continuamente lo stack di esecuzione. Nel caso in cui lo stack sia vuoto, preleverà un messaggio (funzione) dalla coda degli eventi e lo eseguirà.

Il diagramma sottostante mostra la rappresentazione di alto livello del runtime Javascript.

Tempo di esecuzione Javascript

Abbiamo esaminato i diversi componenti nel runtime Javascript. Ora avrai un'idea migliore di come funziona Javascript sotto il cofano. Riassumiamo quanto appreso finora.

Riepilogo

  • Javascript è a thread singolo ed esegue una funzione alla volta.
  • Sfrutta le API Web per eseguire attività complesse in background.
  • Una volta completata l'esecuzione dell'API Web, invia le funzioni di callback alla coda degli eventi.
  • Il ciclo Event controlla continuamente se lo stack Javascript è vuoto. Nel caso in cui lo stack sia vuoto, seleziona la funzione di callback dalla coda degli eventi e la esegue.