Il codice asincrono in JavaScript

In questo video vedremo come funziona il codice asincrono in JavaScript.
Sarà un video un po' tecnico, quindi cercherò di semplificare i vari concetti il più possibile.
In questo video vedremo una veloce panoramica di come un programmatore può lavorare con il codice asincrono, usando diversi tecniche: callback, Promise, funzioni asincrone; ma avendo già dedicato un video ad ognuna di queste maniere (vedi link), nel video parleremo proprio di come funziona il codice asincrono dietro le quinte, dove il programmatore non può arrivare.
Prenderemo confidenza con termini tecnici come: single thread, non blocking, call stack, memory heap, event loop.
JavaScript é un linguaggio single thread. Quindi usa un solo call stack e può eseguire una sola istruzione alla volta, ciò vuol dire che deve completare un comando prima di poter passare al successivo.
Il "problema" sorge quando si deve lavorare con il codice asincrono, come un timer o aspettare la risposta di un server.
JavaScript é un linguaggio non blocking. Questo vuol dire che JS è in grado di eseguire il codice successivo all'attivazione di un timer, senza dover prima aspettare la sua scadenza.

Trascrizione: (click per espandere)
salve a tutti in questo video parleremo di come funziona il codice asincrono in javascript non è un video su come lavorare col codice sincrono perché abbiamo già visto altri video su come si può lavorare quel codice asincrono questo qua vuole essere un video un po' tecnico però cercherò di semplificare il più possibile che vi fa vedere come javascript lavora col codice asincrono cosa c'è dietro al codice asincrono in javascript come dicevo non vado a spiegarvi come si lavora col codice asincrono perché ci sono già dei video dedicati comunque vi metterò tutti i link in descrizione abbiamo visto che come si lavora col codice asincrono usando le call back che erano l'unica maniera possibile finché c'era l'ECMAScript 5 però c'era il problema che quando c'erano troppe call back da maneggiare diventava molto complicato e quindi si parlava anche di callback hell cioè l'inferno delle call back perché appunto era molto complicato riuscire a maneggiarle tutte quante poi con l'ECMAScript 6 nel 2015 sono state introdotte le promise che hanno decisamente aiutato il lavoro con il codice asincrono e infine nel 2017 con l'ECMAScript 8 sono state introdotte le funzioni asincrone che hanno ancora più facilitato la vita allo sviluppatore proprio quando lavora con il codice asincrono usare callback promise o funzioni asincrone nella maggior parte dei casi è una scelta del programmatore perché si può dire che sono complementari perché quello che puoi fare con uno lo puoi fare con l'altro questi tre strumenti però io normalmente consiglio se possibile usare le funzioni asincrone proprio perché sono più semplici da usare comunque abbiamo già visto un video per tutte queste possibilità di lavorare col codice asincrono vi metto il link in descrizione questo qui vuole essere un video dedicato a come javascript lavora col codice asincrono allora javascript è un linguaggio single thread non blocking che significa che ha un solo call stack e un solo memory heap e cosa vuol dire, vuol dire che javascript esegue il codice in ordine di successione quindi riga dopo riga e deve finire un comando prima di poter eseguire il successivo che vuol dire che quando c'è il codice asincrono che quindi è un codice che non può essere eseguito subito perché si deve aspettare un determinato lasso di tempo come quando si usa un timer o si deve aspettare una risposta a una determinata richiesta come per esempio quando si fa una richiesta al server si deve aspettare la risposta del server javascript perché proprio è non blocking non dice ok adesso aspetto qui finché ho la risposta del server o finché il timer scade nel frattempo javascript fa qualcos'altro poi quando arriva la risposta o quando il timer scade allora faccio quello che dovevo fare quando la risposta arrivava o quando il timer scadeva quindi proprio si dice che è non blocking per questo motivo andiamo a vedere adesso per esempio cos'è il call stack adesso io vi ho messo qui uno script dove ci sono un paio di funzioni e c'è la funzione esterna che è questa qui che invoca la funzione interna che è questa qui la funzione interna fa un log poi c'è la funzione esterna che fa un altro log e lo script finisce andiamo a vedere come si vedono il log nella console quando si esegue questo codice come vedete questo qui è il mini script e quando si va a eseguire questo codice io trovo il primo logo che è funzione interna e il secondo log che funziona esterna e questo ok ma come funziona javascript per eseguire questo pezzo di codice andiamo a vedere linea by linea allora prima di tutto javascript invoca una funzione che per come lavora javascript internamente javascript avvolge tutto il codice in una mega funzione e la invoca si chiama iife che è una funzione che si auto invoca e siccome questa funzione non ha nome come vedete si dice che è anonima allora quando javascript comincia ad eseguire il codice ogni funzione viene eseguita dentro al call stack potete pensare al call stack c'è chi lo descrive come il carrello dei piatti al ristorante si mettono sempre piatti uno sopra l'altro e si tolgono i piatti da sopra quindi si mettono i piatti sopra e si tolgono i piatti da sopra non si toglie mai il piatto da sotto e quindi potete pensarlo proprio come il carrello dei piatti quindi si comincia con la funzione anonima poi javascript dice ok io mi trovo che devo eseguire la funzione esterna e quindi invoco la funzione esterna e come vedete una seconda funzione viene inserita dentro allo stack sopra alla funzione anonima la funzione esterna cosa diceva va ad invocare la funzione interna e quindi cosa succede si mette un'altra funzione dentro allo stack sopra tutte le altre cosa fa la funzione interna la funzione interna dice devo fare il console log di questa frase e quindi va eseguire la funzione del console log e la mette sopra tutte le altre e quindi come vedete sono tutti impilate una sopra l'altra eseguita la console log perché non sta chiamando altre funzioni eseguita questa viene tirata via dallo stack e come vedete si tira via sempre da sopra quindi si mette sopra si tira via da sopra poi la funzione interna praticamente ritorna un valore e viene eseguita completamente quindi non serve più la funzione interna e viene tirata via dallo stack quindi si ritorna dentro la funzione esterna la funzione esterna una volta che è stata eseguita la funzione interna che che veniva invocata cosa succede qui dice che vuole fare il console log e quindi si mette il console log dentro allo stack finito di eseguire il console log si tira via il console log poi la funzione esterna praticamente è finita quindi può essere tirata via dallo stack e una volta che tutto il codice è stato eseguito anche la funzione anonima viene tirata via e quindi adesso lo stack è stato liberato certe volte quando ricevete degli errori eseguendo il vostro codice nella console potete vedere lo stack in quel momento per esempio io qui ho messo dentro a questo console log nella funzione interna ho messo una x voglio visualizzare il valore di x ma siccome x non esiste mi trovo che ho un errore sul mio codice e qui come vedete io posso andare a vedere tutto lo stack che c'è nel call stack e come vedete c'era la funzione anonima poi sopra c'era la funzione esterna e poi sopra un ancora c'era la funzione interna stessa cosa si può vedere usando il debugging se adesso io apro source e vado a mettere un breakpoint abbiamo già parlato di come si usa il debugging vi metterò link in descrizione vado a mettere un breakpoint in questa console log e poi vado a eseguire il codice come vedete adesso il codice è fermo in questa console log e come vedete io posso andare a controllare il mio call stack qui sotto in cui come come si vedeva dal grafico sotto a tutto c'è anonimo poi la funziona esterna e poi la funzione interna questa freccina che mi dice dove sono adesso sono nella funzione interna e poi posso andare a finire di eseguire il codice è questo qui è il call stack e questo va tutto bene perché fino a qui abbiamo visto codice sincrono quindi ogni linea veniva eseguita una dopo l'altra cosa succede in questo scenario io ho un pezzo di codice dove vado eseguire una funzione che si chiama lancia timer la funzione lancia timer esegue un console log poi c'è questo settimeout quindi vado a lanciare un timer che il mio timer poi quando scadrà il mio timer farà un log con scritto timer finito e poi ciò questo console log che dice dopo il timer se io vado a vedere il log di questa funzione andiamo a vederli qui che è questa qui andiamo a vedere quando io eseguo la funzione come vedete io ho il primo log che dice prima del timer poi io ho dopo il timer e poi mi trovo timer finito però noi abbiamo detto che javascript esegue linea dopo linea è però abbiamo anche detto che js è non blocking e quindi io non voglio fermare l'esecuzione di tutto il mio codice perché devo aspettare che scada il timer quindi adesso andiamo a vedere come funziona il call stack per questo pezzo di codice allora sempre quando si comincia ad eseguire il codice la funzione anonima che poi va a invocare la funzione lancia timer che poi mi fa mi trova il primo console log e quindi me lo mette nello stack e questo è il log poi il console log finisce tira via dallo stack il secondo mi va a invocare il timer settimeout e me lo mette nello stack e poi la riga viene letta ed eseguita e me la tira via dallo stack come vedete io non ho ancora log qui e infine mi trovo un altro console log e me lo mette nello stack mi trovo il mio logo dopo il timer poi console log lo tira via dallo stack lancia timer lo tira via dello stack è anonimo si tira via dallo stack quindi il mio stack è vuoto non ho ancora l'ultimo log quello del timer finito perché il timer non è stato ancora scaduto quando dopo un secondo il timer finisce io mi trovo timer finito e allora qui uno dice sì non ho capito bene perché lo stack è vuoto quindi da dove arriva questo come fa javascript a sapere che deve eseguire quel log e qui entra in funzione tutto il procedimento di come javascript lavora con le funzioni asincrone questa qui è sempre lo script di prima questo è il mio call stack ma come vedete adesso ci sono altri tool che javascript usa allora c'è il web o node apis quindi web quando si usa il browser o node quando si usa node js bene o male funziona alla stessa maniera poi c'è la queue che è questa qui che che è la coda e poi c'è l'event loop e adesso andiamo a vedere la stessa successione di prima come funziona però perché appunto è un codice asincrono quando javascript comincia ad eseguire tutto il codice lancia sempre la funzione anonima poi c'è lancia timer quindi invoca lancia timer poi c'è la console log come prima e vedo il mio log qui la console log poi viene tirata via dallo stack poi c'è il settimeout che è questo qui quando il settimeout viene tirato via dallo stack la sua callback la callback che c'è dentro qui viene inserita dentro a diciamo che il browser se la tiene in mente c'è il timer che deve eseguire questa callback adesso me la tengo in mente finché il timer scade quindi dopo javascript può tirare via il settimeout dallo stack poi quindi c'è il secondo console log che dice dopo il timer tira via il console log dal call stack tira via lancia timer e tira via l'anonimo e come vedete la call back è tenuta in vita diciamo tra virgolette dal browser quando il timer scade quindi dopo un secondo però potete anche pensare che magari non è un timer è una chiamata ai server java script fa la chiamata al server e si tiene in attesa della sua risposta dentro qui finché non c'è la risposta poi quando c'è la risposta o il timer scade mette la call back nella coda e poi entra in gioco l'event loop pensatelo a un loop che controlla che il call stack sia vuoto e che controlla che la coda sia vuoto lui è sempre lì che controlla questi due e quando il call stack è vuoto allora va a controllare la coda se nella coda c'è una funzione che deve essere eseguita la prende la mette nel call stack e come vedete l'event loop me lo mette nel call stack e nel momento in cui viene eseguita mi trovo il mio logo timer finito quindi l'event loop è sempre lì che controlla che il call stack sia vuoto e quando il call stack è vuoto prende se c'è qualcosa di codice da eseguire nella coda e me lo mettere nel call stack javascript poi lo va a eseguire la coda può essere adesso qui abbiamo visto un esempio con una sola callback ma potrebbero esserci più callback in coda e quindi ogni volta sta lì sempre l'event loop appena il call stack è vuoto prende che se c'è qualcosa nella coda la meta in call stack e la esegue e questo e come funziona il codice asincrono dal punto di vista di javascript quindi tutte queste cose che non possono essere controllate dal programmatore perché lavorano dietro alle quinte fa funzionare il codice asincrono in javascript in maniera non blocking con un solo single thread

adesso vi faccio vedere che io ho detto sì che c'è quell'event loop che è sempre lì che aspetta che il call stack sia vuoto per poter mettere la callback nel call stack ed eseguirla quindi adesso andiamo a vedere un esempio in cui questo dimostra che se il mio call stack non è un vuoto la call back che è in attesa nella coda non può essere eseguita allora io ho questo mini script qui che quando viene eseguito prende il time stamp di questo momento e viene salvato in questa variabile now e dopo viene eseguito questo timer e dentro il timer vado a fare il log dell'ora in cui viene eseguita la call back del mio timer meno il now che avevo preso qui quindi siccome questo timer dovrebbe scadere dopo 50 millisecondi dovrei trovarmi pressappoco 50 52 millisecondi da questa differenza qui andiamo a vedere e come vedete 50 millisecondi esatti se lo ritrova 52 millisecondi comunque grosso modo mi trovo che la call back è stata eseguita proprio quando il timer è scaduto però cosa succede se io adesso metto in gioco un for loop che ha un miliardo di iterazioni siccome questo il for loop dovrà venire eseguito tutto in un colpo non è detto che quando il mio timer scade il call stack sia vuoto in maniera che possa essere eseguita la mia callback del timer andiamo a vedere come vedete adesso io ho 583 millisecondi questo vuol dire che con la scusa che il mio call stack non era vuoto perché stava eseguendo tutte questo miliardo di iterazioni quando il mio timer è scaduto la call back del timer era inattesa dentro la coda e l'event loop era lì che aspettava che il call stack si svuotasse che quindi il mio ciclo for si esaurisse in maniera da poter mettere la call back dentro al call stack ed eseguirla io penso di aver dato una panoramica di come javascript lavora col codice asincrono spero di essere stato più di averlo spiegato nella maniera più semplice possibile con questo è tutto ci sentiamo la prossima ciao

File del video: