Cos’è il modello Bloccante e non Bloccante?
Callback e programmazione Asincrona

Tempo di lettura: 5 minuti
Differenze operazioni bloccanti e non bloccanti. fondamenti di Node.js

Perché Node.js è cosi veloce?

La velocità di Node.js è dovuta principalmente a due fattori:

  • Il motore V8 di Google Chrome, che interpreta il codice Javascript
  • La capacità di gestire le operazioni in modo non bloccante (tra poco ti spiegherò meglio)

Il motore V8

Quando Google ha rilasciato per la prima volta il motore ultra veloce V8 di Chrome, nella comunità informatica c’è stato molto scalpore. Era stato creato l’interprete Javascript più veloce al mondo! Per giunta, veniva rilasciato in licenza open source, quindi chiunque poteva riusarlo nei propri progetti.

Prima di allora, la maggior parte dei browser leggeva e interpretava il codice Javascript un poco alla volta, in modo del tutto inefficiente. Il browser impiegava un sacco di tempo per convertire il codice in linguaggio macchina, comprensibile al processore.

Motore V8 di Chrome interpreta il codice Javascript su Node.js
Motore V8 di Chrome interpreta il codice Javascript su Node.js

Il motore V8 di Google Chrome, che è stato riutilizzato da Node.js, funziona in modo completamente diverso. È altamente ottimizzato ed esegue un tipo di compilazione chiamata  JIT (Just In Time). Trasforma rapidamente il codice Javascript in linguaggio macchina e lo ottimizza attraverso processi complessi (inlining del codice e copy elision).

Non c’è bisogno di sapere come funziona il V8 per poter utilizzare Node.js. L’unico cosa che devi sapere è che permette l’esecuzione di Javascript in modo ultra veloce!

Modello non bloccante

Node.js, come Javascript, si basa sul concetto di eventi, permette di gestire le operazioni in modo del tutto non bloccante (programmazione asincrona).

  Sai già la differenza tra operazioni bloccanti e non bloccanti?
No?

Beh, ecco allora una piccola spiegazione.

Differenze tra modello bloccante e modello non bloccante

Immagina di dover creare un programma che scarichi un file da internet e a download completato mostri un messaggio.

Con il modello bloccante (programmazione sincrona) potremmo schematizzare il codice in questo modo:

Scarica il file
Mostra il messaggio
Fa qualcos'altro

Fin qui nulla di nuovo, le azioni sono eseguite in ordine quindi le righe devono essere lette dall’alto verso il basso:

  1. Il programma avvia il download di un file da internet
  2. Il programma mostra il messaggio di download completato
  3. A questo punto il programma può occuparsi di qualcos’altro (ad esempio, scaricare altri file).

Adesso vediamo lo stesso programma con la versione non bloccante:

Scarica il file
Appena termina il download, mostra il messaggio
Fa qualcos’altro

Questa volta però, il programma non eseguirà più le operazioni nell’ordine in cui le abbiamo scritte!

Farà questo:

  1. Il programma avvia il download del file da internet
  2. Il programma fa altre cose
  3. Quando il download è finito, il programma mostra il messaggio di download completato

L’esecuzione del programma può essere rappresentata così:

Come funziona nodejs modello bloccante non bloccante, programmazione sincrona e asincrona
Programmazione asincrona e modello non bloccante

Questo è esattamente il funzionamento di Node.js!

Appena scatta l’evento “Download completato“, viene richiamata una particolare funzione che eseguirà il codice successivo al download (nel nostro caso mostrarne il messaggio), questa funzione prende il nome di callback.

Il modello non bloccante con Node.js

Proviamo a portare quanto detto finora in un vero codice scritto in Node.js:

request('http://www.site.com/file.zip', function (error, response, body) {
  console.log("Download completato!");
});

console.log("Mentre aspetto eseguo il resto del codice...");

La richiesta di download viene lanciata per prima (riga 1). Successivamente, il programma non resta in attesa ma continua ad eseguire altre operazioni, nel nostro caso un console log (riga 5). Non appena il download termina, il programma passa alla riga 2 e visualizza “Download completato!”.

  Aspetta un momento!

Una funzione prende in ingresso un’altra funzione?!

Che roba è questo codice?

Non farti prendere dal panico! Quello che hai appena visto è un esempio di callback.

In Javascript è piuttosto comune passare una funzione come parametro di ingresso ad un’altra. In questo caso stiamo dicendo a Node.js: “Esegui questa funzione al termine del download“.

Nel nostro caso non abbiamo dato un nome alla nostra funzione, per questo viene chiamata funzione anonima. Per rendere più chiaro il codice potremo separarlo, in ogni caso il risultato è identico:

// Il risultato è identico al precedente codice
var callback = function (error, response, body) {
  console.log ('Download completato!');
});

request ('http://www.site.com/file.zip', callback);
console.log ('Mentre aspetto eseguo il resto del codice...');

È più leggibile?

Adesso la funzione di callback viene salvata in una variabile. Cosi facendo l’abbiamo semplicemente dichiarata ma, come per tutte le funzioni, non viene eseguita fintanto che non viene richiamata effettivamente.

Ora non ci resta che usarla: passiamola come parametro alla funzione request(). Sarà come dire a Node.js: “Non appena terminata il download del file, richiama questa funzione di callback“.

Questo secondo codice ti piace di più?

Beh sappi che in realtà agli sviluppatori Javascript piace scrivere le funzioni di callback come nel primo, quindi come funzioni anonime direttamente dentro i parametri della funzione.

Lo so, inizialmente potrà sembrarti un po’ strano ma ben presto ti abituerai anche tu!

  Perché questo piccolo cambiamento dovrebbe rendere Node.js più veloce?

A te sembrerà solo un modo per rendere il codice più complicato!

Ti avevo avvisato che Node.js non sarebbe stato “semplice”, ma ti assicuro che ne vale davvero la pena!

Adesso ti spiego come mai le callback sono così importanti per non bloccare l’esecuzione del codice.

Immagina di modificare il codice precedente per fargli scaricare 2 file:

var callback = function (error, response, body) {
  console.log ('Download completato!');
});

request ('http://www.site.com/file.zip', callback);
request ('http://www.site.com/otherfile.zip', callback);

In un modello bloccante, il programma eseguirebbe questo:

  1. Avvia il download del primo file.
  2. Aspetta che finisca.
  3. Lancia il download del secondo file
  4. Aspetta che finisca.

Mentre Node. js, con il suo modello non bloccante, avvierebbe i due download contemporaneamente!

Il programma non rimane in attesa che il primo file download finisca e passerebbe subito alle operazioni successive.

In un modello non bloccante (come Node.js), i 2 file vengono caricati contemporaneamente e il tutto finisce più velocemente.

Esempio operazione bloccante non bloccante con Node.js
Download di un file con programmazione sincrona (bloccante) e programmazione asincrona (non bloccante)

Noti delle differenze?

Beh, penso proprio di si!

Il modello non bloccante porta enormi benefici sui tempi di esecuzione, infatti Il programma scarica entrambi i file nello stesso momento.

Questo era un esempio molto banale ma pensa ad applicazioni web reali. Anche i più siti comuni o i servizi API eseguono una lunga serie di operazioni bloccanti.
Ad esempio:

  • Le query sul database
  • Lettura di file dall’hard disk
  • Richiesta dai dati da servizi esterni, come le API di Facebook o Twitter

In breve: Node.js aumenta l’efficienza del nostro server. Non rimane in attesa che una operazione bloccante finisca (es. una query), ma continua ad eseguire il resto del codice!

Conclusioni: cosa devi sapere prima di iniziare ad usare Node.js?

  • Node.js è rapidamente diventato un valido sostituto ai più classici linguaggi e framework backend come PHP, Python/Django, Ruby on rails, ecc.
  • Scrivere applicazioni web usando Node.js non è semplice, richiede uno studio approfondito di Javascript dato che tutto si basa sugli eventi.
  • Node è riconosciuto e apprezzato per la sua velocità: usa il modello non bloccante e questo permette di sfruttare al meglio i tempi e le risorse del server!

L’articolo che hai appena letto fa parte di una lunga serie di guide e tutorial tradotte in italiano e distribuite gratuitamente. Questo articolo si basa sui testi inglesi di Mathieu Nebra (Ultra fast applications using Node.js), con licenza CC BY-NC-SA.