Comprensione del ciclo degli eventi


135

Ci sto pensando e questo è quello che mi è venuto in mente:

Vediamo questo codice qui sotto:

console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);

Arriva una richiesta e il motore JS inizia a eseguire il codice sopra per gradi. Le prime due chiamate sono chiamate di sincronizzazione. Ma quando si tratta di setTimeoutmetodo, diventa un'esecuzione asincrona. Ma JS ritorna immediatamente da esso e continua l'esecuzione, che si chiama Non-Blockingo Async. E continua a lavorare su altri ecc.

I risultati di questa esecuzione sono i seguenti:

ACDB

Quindi sostanzialmente il secondo è setTimeoutstato completato per primo e la sua funzione di callback viene eseguita prima della prima e questo ha senso.

Stiamo parlando di un'applicazione a thread singolo qui. JS Engine continua a eseguirlo e, a meno che non completi la prima richiesta, non passerà alla seconda. Ma il bello è che non aspetterà che le operazioni di blocco setTimeoutvengano risolte, quindi sarà più veloce perché accetta le nuove richieste in arrivo.

Ma le mie domande sorgono sui seguenti elementi:

# 1: se stiamo parlando di un'applicazione a thread singolo, quale meccanismo elabora setTimeoutsmentre il motore JS accetta più richieste ed esegue? In che modo il singolo thread continua a funzionare su altre richieste? Cosa funziona setTimeoutmentre altre richieste continuano ad arrivare e vengono eseguite.

# 2: Se queste setTimeoutfunzioni vengono eseguite dietro le quinte mentre arrivano e vengono eseguite più richieste, che cosa esegue le esecuzioni asincrone dietro le quinte? Come si chiama questa cosa di cui parliamo EventLoop?

# 3: Ma non dovrebbe essere messo l'intero metodo in EventLoopmodo che l'intera cosa venga eseguita e venga chiamato il metodo di callback? Questo è quello che capisco quando parlo di funzioni di callback:

function downloadFile(filePath, callback)
{
   blah.downloadFile(filePath);
   callback();
}

Ma in questo caso, come fa JS Engine a sapere se si tratta di una funzione asincrona in modo che possa mettere il callback nel EventLoop? Forse qualcosa come la asyncparola chiave in C # o una sorta di attributo che indica che il metodo che JS Engine prenderà è un metodo asincrono e dovrebbe essere trattato di conseguenza.

# 4: Ma un articolo dice abbastanza contrario a quello che stavo indovinando su come potrebbero funzionare le cose:

Event Loop è una coda di funzioni di callback. Quando viene eseguita una funzione asincrona, la funzione di richiamata viene inserita nella coda. Il motore JavaScript non inizia l'elaborazione del ciclo degli eventi fino a quando non viene eseguito il codice dopo l'esecuzione di una funzione asincrona.

# 5: E qui c'è questa immagine che potrebbe essere utile, ma la prima spiegazione nell'immagine sta dicendo esattamente la stessa cosa menzionata nella domanda numero 4:

inserisci qui la descrizione dell'immagine

Quindi la mia domanda qui è di ottenere alcuni chiarimenti sugli elementi sopra elencati?


1
Le discussioni non sono la metafora giusta per gestire questi problemi. Pensa agli eventi.
Denys Séguret,

1
@dystroy: un esempio di codice per illustrare quella metafora dell'evento in JS sarebbe bello da vedere.
Tarik,

Non vedo quale sia esattamente la tua domanda qui.
Denys Séguret,

1
@dystroy: la mia domanda qui è di ottenere alcuni chiarimenti sugli elementi sopra elencati?
Tarik,

2
Il nodo non è a thread singolo ma non ha importanza per te (a parte il fatto che riesce a fare altre cose mentre il tuo codice utente viene eseguito). Viene eseguito al massimo un solo callback nel codice utente alla volta.
Denys Séguret,

Risposte:


85

1: Se stiamo parlando di un'applicazione a thread singolo, quali processi setTimeouts mentre il motore JS accetta più richieste ed esegue? Quel singolo thread non continuerà a funzionare su altre richieste? Quindi chi continuerà a lavorare su setTimeout mentre altre richieste continuano ad arrivare e vengono eseguite.

C'è solo 1 thread nel processo del nodo che eseguirà effettivamente il JavaScript del tuo programma. Tuttavia, all'interno del nodo stesso, ci sono in realtà diversi thread che gestiscono l'operazione del meccanismo del loop di eventi, e questo include un pool di thread IO e una manciata di altri. La chiave è che il numero di questi thread non corrisponde al numero di connessioni simultanee gestite come farebbero in un modello di concorrenza thread per connessione.

Ora sull'esecuzione di setTimeouts, quando invochi setTimeout, tutto il nodo non fa altro che aggiornare una struttura di dati delle funzioni da eseguire in un momento futuro. Fondamentalmente ha un sacco di code di cose che devono essere fatte e ogni "tick" del loop di eventi ne seleziona una, la rimuove dalla coda e la esegue.

Una cosa fondamentale da capire è che il nodo si basa sul sistema operativo per la maggior parte del sollevamento pesante. Quindi le richieste di rete in entrata vengono effettivamente monitorate dal sistema operativo stesso e quando il nodo è pronto a gestirne una, utilizza semplicemente una chiamata di sistema per richiedere al sistema operativo una richiesta di rete con i dati pronti per essere elaborati. Gran parte del nodo IO "lavoro" fa o "Hey OS, hai una connessione di rete con i dati pronti per la lettura?" o "Ehi OS, una qualsiasi delle mie eccezionali chiamate al filesystem ha i dati pronti?". In base al suo algoritmo interno e al design del motore del ciclo di eventi, il nodo selezionerà un "segno di spunta" di JavaScript per eseguirlo, eseguirlo, quindi ripetere nuovamente il processo. Questo è ciò che si intende con il ciclo degli eventi. Fondamentalmente il nodo determina sempre "qual è il prossimo bit di JavaScript che dovrei eseguire?", Quindi eseguirlo.setTimeoutoppure process.nextTick.

2: Se questi setTimeout verranno eseguiti dietro le quinte mentre più richieste arrivano e vengono e vengono eseguite, la cosa che esegue le esecuzioni asincrone dietro le quinte è quella di cui stiamo parlando su EventLoop?

Nessun JavaScript viene eseguito dietro le quinte. Tutto il JavaScript nel tuo programma viene eseguito in primo piano, uno alla volta. Quello che succede dietro le quinte è che il sistema operativo gestisce l'IO e il nodo attende che sia pronto e il nodo gestisce la sua coda di javascript in attesa di esecuzione.

3: Come può JS Engine sapere se è una funzione asincrona in modo da poterla inserire in EventLoop?

Esiste un set fisso di funzioni nel core del nodo che sono asincrone perché fanno chiamate di sistema e il nodo sa quali sono perché devono chiamare il sistema operativo o C ++. Fondamentalmente tutte le I / O della rete e del filesystem e le interazioni dei processi figlio saranno asincrone e l'UNICO modo in cui JavaScript può far eseguire al nodo l'esecuzione asincrona di un nodo è invocare una delle funzioni asincrone fornite dalla libreria core del nodo. Anche se stai usando un pacchetto npm che definisce la propria API, al fine di produrre il ciclo degli eventi, alla fine quel codice del pacchetto npm chiamerà una delle funzioni asincrone del nodo del nodo ed è allora che il nodo sa che il segno di spunta è completo e può iniziare l'evento algoritmo loop di nuovo.

4 Il Loop eventi è una coda di funzioni di callback. Quando viene eseguita una funzione asincrona, la funzione di richiamata viene inserita nella coda. Il motore JavaScript non inizia l'elaborazione del ciclo degli eventi fino a quando non viene eseguito il codice dopo l'esecuzione di una funzione asincrona.

Sì, questo è vero, ma è fuorviante. La cosa fondamentale è che il modello normale è:

//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
  //The code inside this callback function will absolutely NOT run in tick 1
  //It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done

Quindi sì, potresti bloccare totalmente il loop degli eventi semplicemente contando i numeri di Fibonacci in sincronia tutti in memoria tutti nello stesso segno di spunta, e sì che congelerebbe totalmente il tuo programma. È la concorrenza cooperativa. Ogni tick di JavaScript deve generare il loop degli eventi entro un ragionevole lasso di tempo o l'architettura generale non riesce.


1
Diciamo che ho una coda che richiederà l'esecuzione del server 1 minuto, e la prima cosa è stata una funzione asincrona che è terminata dopo 10 secondi. Andrà alla fine della coda o si sposterà nella linea nell'istante in cui è pronto?
ilyo

4
Generalmente andrà alla fine della coda, ma la semantica di process.nextTickvs setTimeoutvs setImmediateè leggermente diversa, anche se non dovresti davvero preoccupartene. Ho un post sul blog chiamato setTimeout e amici che approfondisce i dettagli.
Peter Lyons,

Puoi per favore elaborare? Diciamo che ho due callback e il primo ha un metodo changeColor con tempo di esecuzione di 10mS e un setTimeout di 1 minuto e il secondo ha un metodo changeBackground con tempo di esecuzione di 50ms con un setTimeout di 10 secondi. Sento che changeBackground sarà prima in coda e changeColor sarà il prossimo. Successivamente, il ciclo degli eventi seleziona i metodi in modo sincrono. Ho ragione?
SheshPai,

1
@SheshPai è troppo confuso per tutti discutere di codice quando scritto in paragrafi in inglese. Pubblica una nuova domanda con uno snippet di codice in modo che le persone possano rispondere in base al codice anziché a una descrizione del codice, il che lascia molta ambiguità.
Peter Lyons,

youtube.com/watch?v=QyUFheng6J0&spfreload=5 questa è un'altra buona spiegazione di JavaScript Engine
Mukesh Kumar

65

Esiste un fantastico video tutorial di Philip Roberts, che spiega il loop degli eventi javascript nel modo più semplicistico e concettuale. Ogni sviluppatore javascript dovrebbe dare un'occhiata.

Ecco il link del video su Youtube.


16
L'ho visto ed è stata davvero la migliore spiegazione di sempre.
Tarik,

1
A deve guardare video per i fan e gli appassionati di JavaScript.
Nirus,

1
questo video cambia il mio live ^^
HuyTran il

1
è venuto vagando qui .. e questa è una delle migliori spiegazioni che ho avuto ... grazie per aver condiviso ...: D
RohitS

1
È stato un colpo d'occhio
HebleV

11

Non pensare che il processo host sia a thread singolo, non lo sono. Ciò che è single-thread è la parte del processo host che esegue il tuo codice javascript.

Eccetto per lavoratori in background , ma questi complicano lo scenario ...

Quindi, tutto il tuo codice js viene eseguito nello stesso thread e non è possibile ottenere due parti diverse del codice js da eseguire contemporaneamente (quindi, non si ottiene nigthmare di concorrenza da gestire).

Il codice js che sta eseguendo è l'ultimo codice che il processo host ha raccolto dal ciclo degli eventi. Nel tuo codice puoi sostanzialmente fare due cose: eseguire istruzioni sincrone e pianificare funzioni da eseguire in futuro, quando si verificano alcuni eventi.

Ecco la mia rappresentazione mentale (attenzione: è solo che, non conosco i dettagli di implementazione del browser!) Del tuo codice di esempio:

console.clear();                                   //exec sync
console.log("a");                                  //exec sync
setTimeout(                //schedule inAWhile to be executed at now +1 s 
    function inAWhile(){
        console.log("b");
    },1000);    
console.log("c");                                  //exec sync
setTimeout(
    function justNow(){          //schedule justNow to be executed just now
        console.log("d");
},0);       

Mentre il codice è in esecuzione, un altro thread nel processo host tiene traccia di tutti gli eventi di sistema che si verificano (clic sull'interfaccia utente, lettura dei file, pacchetti di reti ricevuti ecc.)

Al termine, il codice viene rimosso dal ciclo degli eventi e il processo host torna a controllarlo, per vedere se è necessario eseguire più codice. Il ciclo degli eventi contiene altri due gestori di eventi: uno da eseguire ora (la funzione justNow) e un altro entro un secondo (la funzione inAWhile).

Il processo host ora tenta di abbinare tutti gli eventi accaduti per vedere se ci sono gestori registrati per loro. Ha scoperto che l'evento che justNow sta aspettando è accaduto, quindi inizia a eseguire il suo codice. Quando la funzione justNow esce, controlla il loop degli eventi un'altra volta, ricercando i gestori degli eventi. Supponendo che sia passato 1 s, esegue la funzione inAWhile e così via ....


SetTimeout è tuttavia implementato nel thread principale. Quindi non c'è nulla nel tuo esempio che richieda un thread separato. In effetti, nei browser sono implementate solo le schede in più thread. In una singola scheda tutti i processi, tra cui la realizzazione di più connessioni di rete parallele, l'attesa di clic del mouse, setTimeout, animazioni ecc. Vengono eseguiti nella stessa discussione
slebetman
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.