Vai al contenuto

Node.js, cos’è e come si usa?

Node.js è una piattaforma costruita sul runtime JavaScript V8 di Chrome per sviluppare velocemente e facilmente applicazioni di rete scalabili. Node.js utilizza un modello event-driven e operazioni I/O non-blocking che lo rende leggero ed efficiente, ideale per applicazioni real-time semplici o enterprise per dispositivi distribuiti.

Dopo aver confuso le idee con questa definizione, tento di dare una spiegazione semplificata di cos’è e cosa si può fare con Node.js. Ma prima di farlo, dedico qualche minuto a voi che state leggendo e al vostro rapporto con Javascript.

Se avete iniziato, tempo fa, a sviluppare pagine web tramite codice HTML immagino che – come me – vi siete imbattuti in una cosa divertente chiamata Javascript… Talmente divertente che avete cominciato a usarlo in maniera molto semplice, per aggiungere un po’ di interattività nelle vostre pagine, magari con qualche alert() qua e la. E’ probabile poi che lo sviluppo di siti complessi vi ha spingo ad andare oltre e apprendere un linguaggio di programmazione server side, come PHP, Ruby, JSP: ecco, ha inizio la scrittura di codice “back-end”. Con l’avvento di jQuery e simili, il vostro rapporto con Javascript cambia perchè avete sicuramente compreso che il linguaggio Javascript non è solo un window.open(). Ma c’è qualcosa che vi limita… alla fine sono solo script eseguiti dal browser e per quando jQuery abbia portato quelle novità in più, alla fine vi sentite più un utente che un programmatore Javascript. Ma ecco la svolta: Node.js, ovvero Javascript server-side. Javascript subisce la terza trasformazione: dal DHTML si trasforma in jQuery e adesso in linguaggio server-side!

Approccio event-driven

Il modello su cui si basa Node.js è quello event-driven, anzichè il classico modello a processi o thread concorrenti. Ciò significa che si eseguono azioni solo al verificarsi di un evento, rimane in sleep fino alla notifica del completamento quindi si riattiva per eseguire istruzioni contenute in una funzione callback. Questo meccanismo asincrono è abbastanza noto per chi usa Ajax:  ad esempio, quando un’istruzione richiede un valore ad un server remoto, come nel caso di una chiamata Ajax, l’esecuzione del codice non si ferma in attesa del risultato ma prosegue, “saltando” l’istruzione che effettua la chiamata. Tale istruzione rimane in uno stato di “limbo”, perché è stata eseguita ma non completata: nel frattempo vengono eseguite le istruzioni successive. Quando la chiamata remota viene completata il controllo dell’esecuzione torna all’istruzione che era stata sospesa.

Questo metodo dovrebbe garantire una certa efficienza e dovrebbe permettere l’ottimizzazione delle performance, perché quando lavoriamo sul Web i tempi di attesa tra una richiesta HTTP e l’altra sono tempi molto lunghi rispetto ai tempi di lavoro del processore, per cui sarebbe uno spreco restare in attesa di una risposta.

sincrono-asincrono

Ogni funzione in Node.js è asincrona. Pertanto, tutto ciò che normalmente blocca il thread è invece eseguita in background. Questa è la cosa più importante da ricordare su Node.js. Ad esempio, se si sta leggendo un file del file system, è necessario specificare una funzione di callback da eseguire quando l’operazione di lettura è stata completata.

Blocking Vs Non-Blocking

Di seguito mettiamo a confronto due modalità di programmazione che leggono entrambe il file host contenuto nel folder /etc. Il primo codice è bloccante, perchè attende il completamento della lettura del file e successivamente stampa a video il contenuto del file.

var contents = fs.readFileSync('/etc/hosts');
console.log(contents);
console.log('Fai qualcos\'altro');

Il codice qui sotto invece è non-bloccante, perchè la funzione (anonima) di callback viene chiamata solo dopo aver letto il file. Nel frattempo, il sistema può tranquillamente eseguire le istruzioni successive (stampa a video di una stringa, in questo caso).

fs.readFile('/etc/hosts', function(err, contents) {
     console.log(contents);
});
console.log('Fai qualcos\'altro...');

Ovviamente la funzione anonima del codice sopra, può essere riscritta in modo da poter essere riutilizzata più volte:

var callback = function(err, contents) {
    console.log(contents);
}
fs.readFile('/etc/hosts', callback);
fs.readFile('/etc/inetcfg', callback);

Hello Word in Node.js

Questo è un semplice script in Node che stampa il classico Hello World per ogni richiesta.

var http = require('http');
http.createServer(function (req, res) {
     res.writeHead(200, {'Content-Type': 'text/plain'});
     res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server in attesa su http://127.0.0.1:1337/');

Per avviare il server occorre avere installato node.js correttamente sul proprio pc. Incollate il codice in un file chiamato esempio.js ed eseguitelo con node digitando dalla linea di comando:

% node esempio.js

Seguirà un output “Server in attesa su http://127.0.0.1:1337/” come abbiamo definito nel codice.

Analizziamo una dopo l’altra le righe del nostro semplice server.

  1. require serve a dichiarare di quale libreria abbiamo bisogno
  2. creiamo il server in ascolto sulla porta 1337 dell’URL 127.0.0.1. La funzione createServer accetta come parametro una funzione di callback che viene avviata ogni qualvolta si invia una richiesta al server (cioè si accede tramite browser all’indirizzo http://127.0.0.1:1337)
  3. console.log semplicemente stampa il messaggio di attesa non appena il server viene avviato.

Cos’è l’Event Loop

Riprendiamo il codice già visto poc’anzi. In pratica, non appena avviato il server esempio.js da riga di comando, e aver visualizzato nella console il messaggio di output, Node mette in attesa il server (tramite una sorta di loop) fin quando non si verifica un evento conosciuto e definito, che nel caso del nostro esempio equivale a request. Non appena si verifica un evento, verrà inserito in una coda di eventi e ogni evento presente nella coda verrà eseguito uno alla volta in base all’ordine di arrivo. Solo allora verrà avviata la funzione di callback definita per ogni specifico evento.