Javascript Event Loop

Ciclo degli eventi

Daniele La Martina

Al giorno d'oggi la maggior parte dei siti e applicazioni Web sfrutta almeno un Framework basato su JavaScript, ma come funziona Javascript e come interagisce con il browser? 

In questo articolo andremo a spiegare brevemente alcune caratteristiche di questo linguaggio, sopratutto per quanto riguarda la gestione del ciclo degli eventi. Javascript, come molti sapranno già, è un linguaggio simultaneo a thread singolo, il che significa che può gestire un singolo call stack : diversamente da quello che accade in altri linguaggi per i quali esiste un modello di concorrenza che permette l'interruzione di una porzione di codice di un thread per mandare avanti l'esecuzione di un altro thread (come C e Java, ad esempio)

Confusi?  Iniziamo a prendere confidenza con alcune terminologie che verranno utilizzate in questo articolo: 


  • Call stack: struttura di dati LIFO (Last In First Out) che tiene traccia dell'esecuzione delle funzioni del nostro applicativo. Quando una funzione viene chiamata per l'esecuzione, questa viene inserita nello stack per poi uscirne quando termina e ne viene preso il risultato;

  • Modello concorrente: la concorrenza è una caratteristica dei sistemi di elaborazione, nei quali può verificarsi che un insieme di elaborazioni computazionali (thread) sia in esecuzione nello stesso istante. Tale sistema viene appunto chiamato sistema a concorrenza o sistema concorrente.

  • Heap: una grande regione di memoria (per lo più non strutturata) nella quale vengono allocati gli oggetti e le variabili utilizzate nel nostro applicativo;

  • Queue: ogni runtime JavaScript contiene una coda di messaggi, che è un elenco di messaggi da elaborare e le relative funzioni di callback da eseguire. Quando lo stack ha una capacità sufficiente, un messaggio viene rimosso dalla coda ed elaborato, il  che si traduce nel chiamare la funzione associata e quindi creare un frame di stack. L'elaborazione del messaggio termina quando lo stack diventa di nuovo vuoto.

  • Runtime (tempo di esecuzione): momento in cui un programma viene eseguito, in contrapposizione ad altre fasi del ciclo vitale di un applicativo (ovvero al compile-time, al deployment-time e al loading-time).

  • Messaggi: nei browser web, e nei framework server ( come ad esempio nodeJS)  i messaggi sono aggiunti ogni qualvolta si verifica un evento al quale è associato un "EventListener", ovvero un metodo preposto ad essere attivato quando si verifica il suddetto evento.


Il Ciclo degli Eventi


Le richieste di rete possono essere lente, le richieste di immagini possono essere lente, ma per fortuna le richieste del server possono essere fatte tramite funzioni asincrone, ovvero eseguendo una porzione di codice fornendo in anticipo un altro metodo che verrà eseguito appena finito il task in corso.

Queste funzioni sono le Callback asincrone, metodi che vengono eseguiti immediatamente dopo l'esaurimento del metodo in esecuzione e quindi non possono essere inserite immediatamente nello stack a differenza delle funzioni.

Dove vanno allora e come vengono gestite?

Le API del browser, (thread creati dal browser implementato in C++ per gestire eventi asincroni come gli eventi DOM, richieste http, setTimeout etc.) svolgono un ruolo importantissimo, trasferendo le callback nella queque al termine dell'esecuzione del metodo chiamante.

Il ruolo principale del ciclo degli eventi è tenere sotto controllo sia lo stack che la coda delle attività, spingendo il primo task della coda nello stack ogni volta che quest'ultimo è vuoto..


Event Loop
 

Perché è importante tutto ciò?

Ogni messaggio e/o callback vengono sempre elaborati completamente prima della presa in carico, ed esecuzione, di qualsiasi altro task nella coda delle attività ma, nei browser Web, tutti i messaggi vengono aggiunti ogni volta che si verifica un evento e ad esso è associato un listener di eventi, ma se non è presente alcun listener preposto ad ascoltare quel tipo di evento, l'evento stesso viene perso nel nulla.

Quindi un click su un elemento con un gestore eventi 'click' aggiungerà un messaggio, allo stesso modo di qualsiasi altro evento.

La chiamata di questa funzione di callback funge da frame iniziale nello stack di chiamate e, poiché Javascript e a thread singolo, l'ulteriore polling ed elaborazione dei messaggi viene interrotto in attesa del ritorno di tutte le chiamate nello stack. Le chiamate di funzione successive ( sincrone) aggiungono nuovi frame di chiamata allo stack.


In sintesi

Il ciclo degli eventi in Javascript quindi controlla che ogni funzione presente all'interno del codice venga eseguita elaborando tutto ciò che trova nello stack di chiamate e, una volta non c'è nulla, spingendo nuovamente nello stack di chiamate le funzioni che si trovano nella coda dei messaggi.

Non dobbiamo quindi aspettare che funzioni come setTimeout, fetch o altre cose facciano il proprio lavoro, perché sono fornite dal browser e vivono sui propri thread. Ad esempio, se si imposta il setTimeout su 2 secondi, non è necessario attendere 2 secondi: l'attesa avviene altrove. Il ciclo degli eventi eseguirà prima le funzioni e dopo quelle asincrone una volta che queste vengono inserite nella coda dei messaggi e lo stack delle chiamate è vuoto.


In conclusione è importante conoscere bene il ciclo degli eventi per non imbatterci in possibili comportamenti anomali derivanti dall'utilizzo di funzioni asincrone per la gestione degli eventi, sulla propria pagina Web o sulla Web app che stiamo sviluppando. 


Odoo • Immagine e testo

Daniele La Martina

- Jr. Developer -