Dopo aver compreso il meccanismo dell'event loop è utile soffermarci su un'importante considerazione: in JavaScript le operazioni di input e output non sono bloccanti (differentemente da quanto accade in altri linguaggi).
Il principio è abbastanza semplice: tutto ciò che riguarda l'esecuzione di codice JavaScript puro viene eseguito all'interno di ciascun ciclo dell'event loop e non è interrompibile; ogni interazione che coinvolge l'ambiente di esecuzione viene eseguita con l'approccio ad eventi tramite la coda di messaggi.
Quindi richieste come il caricamento di dati dal server o la scrittura su un database sono operazioni asincrone il cui completamento inserirà una richiesta di esecuzione della relativa funzione di callback nella coda dei messaggi.
Questo aspetto è uno dei punti di forza di JavaScript che consente di ottenere elevate prestazioni nella gestione di più richieste contemporanee pur mantenendo un unico thread di elaborazione, come avviene, ad esempio, nella gestione delle richieste HTTP in node.js.
Page rendering
In questo contesto ci aspetteremmo che anche il rendering di una pagina HTML sia asincrona, essendo il DOM un componente dello specifico ambiente di esecuzione del browser. In realtà in generale le cose non stanno così. Ce ne possiamo rendere conto eseguendo il seguente codice:
var msgDisplay = document.getElementById("msgDisplay");
msgDisplay.innerHTML = "Elaborazione in corso: fase 1";
for(var i = 0;i<999999999;i++){}
msgDisplay.innerHTML = "Elaborazione in corso: fase 2";
for(var i = 0;i<999999999;i++){}
msgDisplay.innerHTML = "Fine elaborazione";
L'utente vedrà a video soltanto l'ultimo messaggio, perdendosi tutti i messaggi precedenti. Infatti, la modifica del contenuto di un nodo del DOM non si riflette immediatamente in un'operazione di rendering, ma viene recepita soltato al termine del ciclo di esecuzione di JavaScript, rendendo praticamente impercettibili le variazioni precedenti all'ultima.
Un modo per aggirare il problema consiste nell'utilizzare setTimeout()
come meccanismo per interrompere una elaborazione intensiva e dare spazio all'aggiornamento della pagina Web, come mostrato di seguito:
var msgDisplay = document.getElementById("msgDisplay");
function fase1() {
for(var i = 0;i<999999999;i++){}
msgDisplay.innerHTML = "Elaborazione in corso: fase 2";
setTimeout(fase2,0);
}
function fase2() {
for(var i = 0;i<999999999;i++){}
msgDisplay.innerHTML = "Fine elaborazione";
}
msgDisplay.innerHTML = "Elaborazione in corso: fase 1";
setTimeout(fase1,0);
In questo caso, ciascuna fase di elaborazione viene incapsulata all'interno di un ciclo di elaborazione sfruttando un timer, il cui compito consiste semplicemente nell'inserire nella coda dei messaggi un riferimento all'esecuzione delle varie fasi dell'elaborazione stessa.