‫Istruttore: Bentornato.
‫Impariamo ora tutto sul ciclo degli eventi, che
‫è il cuore di Node. js architettura.
‫E questa è probabilmente la lezione
‫più importante di questa sezione, quindi assicurati di aver
‫compreso davvero tutto ciò che ti mostro durante questo video.
‫Quindi iniziamo.
‫Quindi, ecco un diagramma simile all'ultima lezione in
‫modo che sappiamo esattamente di cosa stiamo parlando qui.
‫Quindi, siamo ancora in un processo Node nel singolo thread in
‫cui viene eseguito il ciclo degli eventi, ok?
‫Ora, la prima cosa che devi
‫sapere è che il ciclo degli eventi è il
‫punto in cui viene eseguito
‫tutto il codice dell'applicazione che si trova all'interno delle funzioni di callback.
‫Quindi, in pratica, tutto il codice che non è codice di primo livello verrà
‫eseguito nel ciclo degli eventi.
‫Alcune parti potrebbero essere scaricate nel pool di thread come
‫abbiamo visto nell'ultima lezione, ma
‫è il ciclo degli eventi che si occupa di tutto questo.
‫Come ho detto prima, è davvero
‫il cuore dell'architettura Node.
‫Ok, ora, come ho detto più volte nella prima parte
‫del corso, Node. js è tutto costruito
‫attorno alle funzioni di callback.
‫Quindi, funzioni che vengono chiamate
‫non appena un lavoro viene terminato in futuro.
‫Ricordati che?
‫E funziona in questo modo perché
‫Node utilizza un'architettura attivata da eventi, che
‫è qualcosa di cui parleremo in uno
‫dei prossimi video.
‫Ma quello che devi sapere per
‫ora è che cose come
‫la nostra applicazione che riceve una richiesta HTTP sul nostro server o un timer in
‫scadenza o un file che sta finendo di leggere, tutti questi
‫emetteranno eventi non appena avranno finito con il
‫loro lavoro, e il nostro evento loop prenderà
‫quindi questi eventi e
‫chiamerà le funzioni di callback associate a ciascun evento.
‫Ok, ha senso?
‫Quindi, di nuovo, il ciclo degli eventi riceve eventi
‫ogni volta che accade qualcosa di
‫importante e quindi chiamerà i callback necessari come
‫definiti nel nostro codice.
‫Quindi, in sintesi, di solito si
‫dice che il ciclo di eventi esegue l'orchestrazione,
‫il che significa semplicemente che riceve eventi,
‫chiama le loro funzioni di
‫callback e scarica le attività più costose nel pool di thread.
‫Ora, come funziona tutto questo dietro le quinte?
‫In quale ordine vengono eseguiti questi callback?
‫Bene, questo è quello che scopriremo dopo.
‫Quindi, ricorda, quando avviamo la nostra applicazione Node, il ciclo
‫degli eventi inizia a essere eseguito immediatamente.
‫Ora, il loop di eventi ha
‫più fasi e ogni fase ha una coda
‫di callback, che sono i callback provenienti dagli
‫eventi che riceve il
‫loop di eventi, proprio come abbiamo detto nell'ultima diapositiva.
‫Ora, in alcuni punti leggerai che
‫c'è solo una coda di callback o una coda di eventi, ma
‫in effetti, come ho detto, il ciclo di eventi ha molte fasi in
‫cui ogni fase ha la sua coda di callback.
‫Quindi, diamo ora uno sguardo alle quattro fasi più importanti.
‫Ci sono una o due altre fasi
‫che vengono utilizzate internamente da
‫Node, ma queste non
‫sono così importanti e non ne parlerò.
‫Quindi, la prima
‫fase si occupa dei callback dei
‫timer scaduti, ad esempio, dalla funzione setTimeout().
‫Quindi, se ci sono funzioni di callback da
‫timer che sono appena scaduti, queste sono le prime
‫ad essere elaborate dal ciclo di eventi.
‫Se un timer scade più tardi durante il tempo
‫in cui viene elaborata una delle
‫altre fasi, allora il callback di quel timer verrà chiamato solo
‫non appena il ciclo di eventi torna a questa prima fase.
‫Ha senso?
‫E funziona così in tutte e quattro le fasi.
‫Quindi, i callback in ogni coda vengono elaborati
‫uno per uno fino a quando non ne rimangono più in
‫coda e solo allora il ciclo degli eventi entrerà nella fase successiva.
‫Successivamente, abbiamo il polling I/O e l'esecuzione di callback I/O.
‫E ancora, ricorda che I/O sta per input/output.
‫Quindi, polling significa fondamentalmente cercare nuovi eventi di
‫I/O pronti per essere
‫elaborati e inserirli nella coda di callback.
‫E ricorda che nel contesto di un'applicazione Node, I/O
‫significa principalmente cose come la rete e l'accesso ai file, quindi è
‫in questa fase in cui probabilmente il 99% del
‫nostro codice viene eseguito,
‫semplicemente perché in una tipica app
‫Node, la maggior parte di ciò che dobbiamo fare è relativo
‫alla rete e anche all'accesso ai file.
‫La fase successiva è per i callback setImmediate e setImmediate
‫è un tipo speciale di timer che
‫possiamo usare se vogliamo elaborare i callback immediatamente dopo la
‫fase di polling ed esecuzione di I/O, che può
‫essere importante in alcuni casi d'uso più avanzati.
‫Va bene.
‫E infine, la quarta fase è per i richiami ravvicinati, che, ancora
‫una volta, non sono così importanti per
‫noi, ma lo metto comunque qui per motivi di completezza.
‫Fondamentalmente, in questa fase, vengono elaborati tutti gli eventi di
‫chiusura, ad esempio per l'arresto di un server Web o di un WebSocket.
‫Quindi, queste sono le quattro fasi nel ciclo degli
‫eventi, ma oltre a queste quattro code di callback che
‫abbiamo appena visto, in realtà ci sono
‫anche altre due code, la coda nextTick() e l'altra coda
‫di microtask, che è principalmente per le promesse risolte.
‫Se non hai familiarità con
‫le promesse, ne parleremo un po' in una sezione successiva.
‫Ad ogni modo, se sono presenti callback
‫in una di queste due code
‫da elaborare, verranno eseguite subito dopo la fine della
‫fase corrente del ciclo di eventi invece
‫di attendere la fine dell'intero ciclo.
‫Va bene?
‫Quindi, in altre parole, dopo ciascuna di queste
‫quattro fasi, se ci sono delle richiamate in queste
‫due code speciali, verranno eseguite immediatamente.
‫Ora, ad esempio, immagina che una promessa si risolva
‫e restituisca alcuni dati da una chiamata API mentre è in
‫esecuzione il callback di un timer scaduto.
‫Quindi, in questo caso, la richiamata della promessa verrà eseguita
‫subito dopo la fine di quella del timer.
‫Va bene?
‫E la stessa logica si applica anche alla coda nextTick(),
‫di cui non abbiamo ancora parlato.
‫Quindi, fondamentalmente, process the nextTick() è una funzione che possiamo usare
‫quando abbiamo davvero, davvero bisogno di eseguire
‫un certo callback subito dopo
‫la fase del ciclo di eventi corrente.
‫È un po' simile a setImmediate, con
‫la differenza che setImmediate viene eseguito solo dopo la
‫fase di callback di I/O.
‫Ciò che è
‫simile, tuttavia, è che entrambi sono per casi d'uso davvero
‫avanzati e probabilmente non ne avremo nemmeno
‫bisogno durante questo corso.
‫Ma comunque, volevo includere anche queste cose più complesse qui
‫in modo da avere gli strumenti di
‫cui hai bisogno se hai davvero bisogno di
‫scavare a fondo in Node. js se vuoi.
‫Bene, e con ciò, abbiamo effettivamente terminato un tick
‫del ciclo degli eventi, e un tick è fondamentalmente solo un ciclo
‫in questo ciclo.
‫Quindi, ora è il momento di decidere se il ciclo
‫deve continuare fino al tick successivo o se il programma deve uscire.
‫E come fa Node a farlo?
‫Bene, è molto semplice.
‫Il nodo controlla semplicemente se ci sono
‫timer o attività di I/O che sono ancora in
‫esecuzione in background e, se non ce ne sono, uscirà dall'applicazione.
‫Ma se ci sono timer o attività di I/O in sospeso,
‫allora continuerà a eseguire il ciclo degli
‫eventi e passerà direttamente al segno di spunta successivo.
‫Quindi, ad esempio, quando
‫ascoltiamo le richieste HTTP in entrata come abbiamo
‫fatto nel nostro progetto Node farm in una sezione precedente,
‫stavamo fondamentalmente eseguendo un'attività di I/O, ed è per
‫questo che il ciclo degli eventi,
‫e quindi, Node. js, continua
‫a funzionare e continua ad ascoltare le nuove richieste
‫HTTP in arrivo invece di uscire dall'applicazione.
‫Inoltre, quando scriviamo o leggiamo un
‫file in background, anche questa è un'attività di
‫I/O, quindi ha senso che l'app non
‫esca mentre sta lavorando con quel file, giusto?
‫Ok, e questo è fondamentalmente ciò che dovresti
‫sapere su Node. js ciclo di eventi.
‫Se hai bisogno di ulteriori dettagli, beh,
‫puoi sempre provare a
‫leggere la documentazione ufficiale di Node, che dovrebbe essere abbastanza
‫facile da capire a questo punto ora
‫che hai già compreso la maggior parte del ciclo di eventi comunque.
‫E voglio solo sottolineare che è
‫davvero, davvero importante per te capire correttamente il ciclo degli
‫eventi in modo da poter scrivere il
‫tuo codice performante e anche eseguire il debug del tuo
‫codice quando qualcosa va storto in un modo inaspettato.
‫E ora, solo per finire, esaminiamo alcune delle
‫cose di cui abbiamo parlato qui.
‫Quindi, in poche parole, la cosa più importante che voglio
‫che tu capisca da questa lezione, e forse
‫da questo intero corso, è che il ciclo
‫di eventi è ciò che rende possibile
‫la programmazione asincrona in Node. js, rendendolo la
‫caratteristica più importante nel design di Node e
‫rendendo Node. js completamente
‫diverso da altre piattaforme.
‫Si occupa di tutti gli eventi in
‫arrivo ed esegue l'orchestrazione scaricando le attività più pesanti nel
‫pool di thread e svolgendo da solo il lavoro più semplice.
‫Inoltre, ricorda che abbiamo bisogno del ciclo degli
‫eventi perché in Node. js tutto funziona in
‫un singolo thread, quindi puoi avere
‫migliaia o milioni di utenti che accedono allo stesso thread contemporaneamente.
‫Ciò rende Node così leggero e scalabile,
‫ma allo stesso tempo presenta il pericolo di
‫bloccare il nostro
‫singolo thread, il che rallenterebbe l'intera
‫app o addirittura si fermerebbe per tutti i tuoi utenti che accedono all'app, giusto?
‫Ora, in altri linguaggi come PHP in esecuzione
‫su un server Apache, in pratica, viene creato un nuovo thread
‫per ogni nuovo utente, che è molto più dispendioso in termini di risorse.
‫Ma d'altra parte, non c'è
‫pericolo di blocco, giusto?
‫Quindi, l'intero modello rende un po'
‫più semplice l'uso di PHP per i principianti, ma
‫ovviamente ha i suoi svantaggi,
‫di cui non parlerò a questo punto.
‫Ad ogni modo, lascia che
‫ti ricordi di nuovo che è tua responsabilità non bloccare il ciclo degli
‫eventi, quindi ecco un paio di linee guida per questo.
‫Prima di tutto, non utilizzare
‫le versioni di sincronizzazione delle funzioni nei
‫moduli fs, crypto o zlib nelle funzioni di callback, ok?
‫Quindi, nel nostro primo progetto, abbiamo effettivamente utilizzato la
‫versione sincrona, ma era nel codice di livello superiore, quindi
‫al di fuori di qualsiasi callback.
‫E poiché quel codice viene eseguito prima ancora che inizi il
‫ciclo degli eventi, beh, non è un problema usare la versione sincrona lì.
‫Inoltre, e questo è probabilmente abbastanza ovvio,
‫non eseguire calcoli molto complessi nel ciclo degli eventi.
‫Quindi, cose come macinare
‫milioni di numeri in loop all'interno di loop o qualcosa del genere.
‫Quindi, fai attenzione con JSON in oggetti molto grandi perché
‫a un certo punto può iniziare a
‫impiegare molto tempo per analizzare o stringere JSON.
‫E infine, non utilizzare espressioni regolari troppo complesse, ad
‫esempio, con più quantificatori nidificati o
‫riferimenti a ritroso, perché di
‫nuovo, possono richiedere più tempo del previsto.
‫Queste sono, ovviamente, solo un paio
‫di linee guida di alto livello, ma ti permetteranno di iniziare
‫sulla strada giusta.
‫Ora, ci sono alcune
‫potenziali soluzioni a questi problemi di blocco, come l'offload
‫manuale nel pool di thread o l'utilizzo di processi figlio,
‫e potremmo parlarne entro la fine del
‫corso o in futuro, ma per
‫ora è importante che capisci e segui questo consiglio
‫di non bloccare davvero il ciclo degli eventi.
‫Va bene.
‫Successivamente, ti darò un piccolo esempio per mostrarti alcune
‫delle cose di
‫cui abbiamo parlato in pratica.