‫Istruttore: Prendiamoci ora un minuto per conoscere
‫la natura asincrona di Node. js,
‫che include argomenti assolutamente fondamentali,
‫come codice sincrono, asincrono, bloccante
‫e non bloccante.
‫E tutto questo sarà davvero importante
‫per capire tutto ciò che sta arrivando in
‫questa sezione.
‫Quindi questo pezzo di codice che abbiamo scritto nell'ultima
‫lezione, per leggere un file e poi salvarne il contenuto in
‫una variabile, era in un cosiddetto modo
‫sincrono, il che significa semplicemente
‫che ogni istruzione viene fondamentalmente elaborata una dopo l'altra, riga per linea.
‫In questo
‫esempio, prima è richiesto il modulo del file
‫system, quindi il file viene
‫letto e quindi registriamo il risultato nella console.
‫Quindi vedi che ogni riga
‫di codice sostanzialmente attende il risultato della riga precedente.
‫Ora, questo può diventare un
‫problema, specialmente con operazioni lente,
‫perché ogni riga blocca l'esecuzione del resto
‫del codice.
‫E quindi diciamo che il
‫codice sincrono è anche chiamato codice di blocco perché, ancora una
‫volta, una certa operazione può essere eseguita
‫solo dopo che quella precedente è terminata.
‫E a causa del modo in cui Node. js è stato
‫progettato, questo si trasforma in un grosso problema, come
‫vedremo in dettaglio nella prossima slide.
‫Quindi la soluzione a questo
‫problema in Node è usare codice asincrono e non bloccante.
‫Quindi, nel codice asincrono, carichiamo un lavoro pesante
‫su cui lavorare sostanzialmente in background.
‫E poi, una volta terminato
‫il lavoro, viene chiamata una funzione di callback
‫che registriamo prima per gestire il risultato.
‫E durante tutto questo
‫tempo, il resto del codice può ancora
‫essere eseguito senza essere bloccato dall'attività
‫pesante, che ora è in esecuzione in background.
‫Quindi ciò significa che possiamo effettivamente rinviare o
‫reagire al futuro
‫per rendere il codice non bloccante
‫e questo, ovviamente, è molto meglio.
‫Ha senso?
‫Quindi, in questo esempio, usiamo la
‫funzione asincrona readFile, che accetta una funzione di callback.
‫Questo inizierà a leggere il file
‫in background per poi passare immediatamente all'istruzione successiva,
‫stampando sulla console il file di lettura delle stringhe.
‫Quindi, di nuovo, vedete, non stiamo bloccando l'esecuzione qui.
‫Quindi, quando il file sarà finalmente letto completamente, verrà chiamata
‫la funzione di callback, e
‫così, i dati che erano stati
‫letti verranno quindi stampati sulla console.
‫Quindi è abbastanza diverso
‫dalla versione sincrona, non è vero?
‫Ora, la domanda qui
‫è, perché in realtà deve essere così?
‫Qual è il problema con il blocco dell'esecuzione del codice in Node. js?
‫O, in altre parole, perché utilizziamo effettivamente il callback così
‫tante volte in Node. js?
‫Bene, scopriamolo.
‫E per capire queste domande, la prima cosa
‫che dobbiamo capire è il fatto
‫che un Node. js, che è
‫dove è in esecuzione la nostra
‫applicazione, c'è solo un singolo thread.
‫E il thread è proprio come una serie di istruzioni
‫che viene eseguita nella CPU del computer.
‫Quindi, in pratica, il thread è il punto
‫in cui il nostro codice viene effettivamente eseguito nel processore di una macchina.
‫Quindi, ricorda, Node. js è fondamentalmente a
‫thread singolo e quindi, per ogni applicazione, c'è solo un thread.
‫Questo è il modo in cui Node. js è stato progettato.
‫Ora, ciò significa che tutti gli
‫utenti che accedono alla tua applicazione utilizzano tutti lo stesso
‫thread, quindi, in pratica, accedono allo stesso thread.
‫E così, ogni volta che interagiscono con l'applicazione,
‫il codice che viene eseguito
‫per ogni utente verrà eseguito tutto nello stesso thread nella
‫stessa posizione nel computer che esegue l'applicazione.
‫E questo è vero non importa
‫se hai cinque utenti, come in questo diagramma, o 5.000 o 5 milioni.
‫Tutto ok?
‫Ora, ciò significa anche che quando un utente
‫blocca il singolo thread con codice
‫sincrono, come abbiamo
‫appena visto prima, tutti gli altri
‫utenti dovranno attendere il termine dell'esecuzione.
‫E questo potrebbe non essere un
‫grosso problema se hai come cinque utenti, ma
‫sicuramente lo sarà per migliaia o addirittura milioni di
‫utenti allo stesso tempo.
‫Quindi, immagina che un utente stia accedendo alla tua applicazione
‫e che ci sia un enorme file sincrono letto nel
‫tuo codice che impiegherà un secondo per caricarsi.
‫Ciò significa, ovviamente, che per quel secondo,
‫tutti gli altri utenti
‫dovranno aspettare perché l'intera esecuzione è bloccata per quel secondo.
‫Quindi, se quegli altri utenti vogliono svolgere alcune semplici
‫attività, come accedere alla tua applicazione
‫o semplicemente richiedere alcuni dati, non saranno in grado di farlo.
‫Dovranno attendere che il file finisca di leggere.
‫Solo quando ciò accadrà saranno finalmente in grado di svolgere
‫i compiti più semplici, uno dopo l'altro.
‫Ora, tieni presente che questa è una versione molto semplificata di ciò che accade
‫davvero dietro le quinte di Node. js, motivo per
‫cui torneremo su tutto questo nella
‫prossima sezione e avremo una comprensione ancora più
‫profonda di come Node. js gestisce il codice asincrono sotto il cofano.
‫Ma a questo
‫punto ti basta questo per capire il concetto.
‫È meglio procedere passo dopo
‫passo e non
‫creare confusione fin dall'inizio, ok?
‫Ad ogni modo, è così che
‫andrebbe a finire la situazione con il codice di
‫blocco sincrono, che è ovviamente un'esperienza terribile per i tuoi utenti.
‫Quindi, è davvero il tuo lavoro come
‫sviluppatore evitare questo tipo di
‫situazioni utilizzando codice asincrono.
‫Quindi, per la stessa situazione, dovremmo, ovviamente, usare la
‫funzione di lettura file asincrona, che invece
‫di bloccare il singolo thread,
‫fa il lavoro pesante in background, dove
‫sostanzialmente rimane fino a quando non ha
‫finito di leggere i dati dal file.
‫Ovviamente poi registriamo anche una funzione di callback
‫da richiamare una volta che i dati sono disponibili.
‫E in questo scenario, tutti gli altri
‫utenti possono quindi svolgere le loro attività in
‫un singolo thread, uno dopo l'altro, mentre il file viene
‫ancora letto in background.
‫Ora, una volta letti i dati, la nostra funzione
‫di callback verrà ovviamente chiamata
‫per essere eseguita nel singolo thread principale per elaborare i dati letti.
‫E questo è tutto.
‫Questa è una panoramica di come Node. js gestisce
‫il comportamento asincrono per implementare il modello di
‫I/O non bloccante di cui abbiamo
‫parlato nella lezione introduttiva, va bene?
‫E I/O sta semplicemente per input-output, che è
‫fondamentalmente cose come accedere al file system e
‫gestire le richieste di rete.
‫Questo è in realtà l'intero motivo per cui Node. js è
‫completamente progettato attorno ai callback,
‫come vedrai durante il corso.
‫In altri linguaggi di programmazione, come PHP, funziona
‫in modo molto diverso perché ottieni, in pratica, un
‫nuovo thread per ogni nuovo utente,
‫che è un paradigma completamente diverso
‫e funziona davvero in modo completamente diverso.
‫Ma il creatore di Node. js ha scoperto che
‫questo modello è la soluzione migliore per la creazione
‫di applicazioni Web altamente performanti e scalabili.
‫Ora, solo come nota finale qui, è importante sapere che,
‫quando usiamo i callback
‫nel nostro codice, ciò non lo rende automaticamente asincrono, va bene?
‫Quindi, passare le funzioni ad altre funzioni
‫è abbastanza comune in
‫JavaScript, ma ovviamente, ancora una volta,
‫ciò non le rende automaticamente asincrone, ok?
‫Funziona in questo modo solo per alcune funzioni nell'API Node,
‫come la funzione readFile e
‫molte, molte altre, come le persone esploreranno in futuro.
‫E ora, giusto per
‫finire, visto che qui stiamo parlando di
‫codice asincrono, solo un'ultima nota sulle funzioni di callback.
‫Quindi, questo modello di callback di cui abbiamo appena
‫discusso, in cui una
‫funzione viene chiamata una volta che quella precedente ha
‫terminato il suo lavoro, può portare rapidamente a codice difficile da leggere e ingestibile.
‫Prendi questo esempio in cui il secondo file
‫letto dipende dal primo,
‫quindi il terzo file letto dipende dal secondo
‫e, infine, vogliamo utilizzare i dati finali
‫per scrivere un file come risultato.
‫Sembra abbastanza confuso, vero?
‫Voglio dire, funzionerà bene, ma
‫è solo difficile ragionare e questo
‫è solo con quattro livelli di profondità.
‫Immagina di avere 10 o 20 livelli, il
‫che in realtà non è così raro.
‫Comunque, questo è quello che chiamiamo l'inferno del richiamo.
‫È un problema così
‫comune che ha già un nome.
‫E notate questa forma triangolare qui?
‫È un segno molto chiaro che sei nell'inferno delle richiamate.
‫Ora, come possiamo effettivamente sfuggire all'inferno delle richiamate?
‫Bene, possiamo usare strumenti più avanzati per la
‫gestione del codice asincrono, come le
‫promesse di ES6 o anche meglio, async/await di ES8.
‫Ora, il modello di cui abbiamo appena
‫parlato sarà sempre lo stesso.
‫Abbiamo solo modi più
‫eleganti per gestire il codice stesso e scriverlo.
‫E c'è un'intera sezione facoltativa
‫più avanti nel corso sulle promesse e anche, async/await, quindi nel
‫caso tu non abbia familiarità con loro.
‫Ma per ora, continueremo a utilizzare i callback
‫perché questo è ciò che Node utilizzava originariamente ed
‫è stato progettato attorno a questo.
‫E ora, detto questo,
‫andiamo avanti e usiamo questo modello asincrono in
‫pratica per la prima volta.