5 motivi per imparare Node.JS

Una sintetica premessa che vi spiega perchè scegliere Node.js per la realizzazione di applicazione server side scalabili e flessibili.

Logo Node.js

Se lavoriamo sul Web è ormai obbligatorio ottimizzare i tempi di produzione e manutenzione del software sfruttando i numerosi framework disponibili in rete. Negli ultimi anni il numero di framework tra cui scegliere è aumentato in modo notevole. Abbiamo piattaforme di sviluppo per praticamente qualsiasi tecnologia, partendo dall’interfaccia di frontend per arrivare fino alla gestione dei dati sul backend. Una delle novità più interessanti degli ultimi anni è sicuramente Node.js, una tecnologia che copre molti degli aspetti diversi di una applicazione Web. Ciò è dovuto al fatto che Node.js si presenta come un linguaggio di programmazione praticamente identico a JavaScript, per cui può essere utilizzato facilmente anche da chi non conosce linguaggi di programmazione più avanzati. Contemporaneamente, pur richiedendo principalmente competenze di Web design, in realtà Node.js nasce allo scopo di realizzare applicazioni server side robuste, scalabili e flessibili che possono essere utilizzate anche in ambito professionale.

Vediamo cinque dei motivi più significativi che fanno di Node.js una delle tecnologie più importanti del momento, che vale sicuramente la pena conoscere e tenere in considerazione.

Compilazione JavaScript  con V8

Il vantaggio più evidente di Node.js è la possibilità di realizzare applicazioni server-side senza dover imparare linguaggi di programmazione “tradizionali”. Ciò permette agli utenti meno esperti, come ad esempio chi si occupa solo di programmazione lato frontend, di realizzare applicazioni Web multiutente gestendo la logica lato server. In altre parole Node.js permette di scrivere codice JavaScript come se fosse un linguaggio di programmazione di pagine dinamiche, realizzando tutto ciò che di solito richiede l’utilizzo di PHP, JSP, ASP o altre tecnologie equivalenti.

Ciò è reso possibile dal motore JavaScript V8 di Google, che interpreta e compila il codice JavaScript, trasformandolo in codice eseguibile sul server. In particolare ciò significa che i sorgenti “JavaScript” non verranno inviati al client, bensì verranno eseguiti producendo l’output HTML da inviare al client, che sarà ovviamente dinamico, cioè adattato alle richieste del client specifico.

Questo modo di lavorare mescola le conoscenze sintattiche JavaScript con le competenze tipiche di chi sviluppa applicazioni server side. Da un punto di vista pratico, ad esempio, ciò significa che dopo aver scritto le nostre “pagine” con Node.js non basterà deployarle sul server, ma dovremo anche avviare (sul server) il programma Node. js che le esegue.

Il codice JavaScript che usiamo per realizzare le applicazioni Node.js è inoltre più flessibile e robusto del codice JavaScript scritto ad uso e consumo di un browser. Questo perché Node.js riconosce molte delle istruzioni ECMAScript che di solito non possiamo utilizzare in un browser per problemi di compatibilità. Al contrario, quando lavoriamo con Node.js, possiamo utilizzare senza alcuna preoccupazione tutte le specifiche  ECMAScript, anche quelle che non sono implementate in modo standard dai diversi produttori di browser. Per un approfondimento sull’argomento rimandiamo a questo articolo.

Programmazione asincrona

Una caratteristica molto interessante di Node.js è l’orientamento verso lo sviluppo di codice asincrono.

Ciò significa che l’esecuzione del codice non avviene in modo sequenziale, eseguendo  un’istruzione dopo l’altra, come nella vecchia programmazione strutturata. In alcuni casi l’esecuzione del codice può saltare “di qua e di là”, soprattutto quando una delle istruzioni rimane in attesa di un risultato che impiega un certo tempo per essere disponibile. 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 modo di lavorare  permette di ottimizzare notevolmente le 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 ogni possibile risposta.

nodejs1

In figura vediamo un esempio di programmazione asincrona. Il flusso di esecuzione del programma è rappresentato dalla freccia rossa, che esegue, nell’ordine indicato, i punti 1,2 ed infine 3. Il codice all’interno delle funzioni setTimeout e setInterval non viene eseguito immediatamente, ma secondo un timing deciso al momento di definire queste funzioni. In questo caso, ad esempio, la funzione  setTimeout verrà eseguita con 5000ms di ritardo, ovvero cinque secondi dopo che il flusso del codice è “passato” per il punto 1. Il caso della funzione  setInterval è  leggermente diverso, perché essa verrà eseguita ogni tot secondi, in questo caso ogni 1000ms (1 secondo).

Ciò significa che dopo aver eseguito il punto 3 in generale non è possibile sapere in che ordine verranno eseguiti i punti 4 e 5. In altre parole nella programmazione asincrona non è possibile dire se il flusso del codice proseguirà sulla linea verde o su quella blu. La risposta dipende dalla situazione specifica. Nel nostro caso, siccome abbiamo impostato un intervallo di 1 secondo sull’esecuzione periodica della funzione  setInterval, questa verrà eseguita prima della setTimeout. In particolare, nell’esempio di figura, il blocco all’interno della funzione setInterval verrà eseguito 5 volte prima che venga eseguito il codice all’interno della setTimeout.

L’esempio appena visto è volutamente semplice: la vera programmazione asincrona di solito implica chiamate verso altri server, oppure l’accesso a risorse sul file system. Ogni volta che l’interprete V8 incappa in una istruzione asincrona è come se il programma facesse partire un altro thread, il quale si occupa di restare in attesa dell’esito dell’istruzione. In realtà non si parla di programmazione multi-threading, ma solamente di architettura asincrona gestita tramite l’uso di opportune callback.

Chi volesse approfondire l’argomento, facendo qualche prova concreta nell’ambito di Node.js, può consultare questa guida.

Abbiamo detto che uno dei vantaggi di Node.js è la possibilità di realizzare applicazioni professionali scalabili. Ciò è dovuto all’architettura modulare che sta alla base di Node.js. Dopo aver installato Node.js possiamo facilmente aggiungere altri moduli, scegliendo sia tra quelli ufficiali che vengono rilasciati con l’istallazione minima, sia quelli sviluppati da altri utenti. La cosa più importante, per quanto riguarda l’architettura modulare, è che ogni applicazione può essere scritta in questo modo, anzi: il paradigma di lavoro di Node.js “spinge” nella direzione di applicazioni prodotte con un approccio modulare. Lavorando con Node.js è molto facile organizzare il lavoro in librerie, importare i nostri moduli e poi riciclarli tra un progetto e l’altro.

I moduli disponibili sin dalla prima installazione, cioè quelli nativi, si trovano all’interno della directory npm. (che sta per Node.js Package Management), mentre i nostri moduli possono essere collocati dove vogliamo. Dopo aver suddiviso un’applicazione in un certo numero di moduli diventa molto facile combinare tra loro i moduli, un po’ come se fossero oggetti nell’ambito del paradigma di programmazione ad oggetti (OOB). Un’applicazione Node.js potrebbe essere realizzata facendo lavorare in concerto un gran numero di moduli, eventualmente distribuiti su macchine diverse, facendoli comunicare tra loro. E’ proprio per questo che è stato scelto il nome Node: ogni componente software si comporta come un piccolo nodo all’interno di una “rete”, la quale può essere facilmente scalata e riproporzionata in base alle esigenze.

Un’introduzione pratica all’uso dei moduli è disponibile qui.

Velocità di trasmissione

Data l’architettura modulare è particolarmente importante che i diversi mattoncini di Node.js siano in grado di comunicare tra loro in modo efficiente. Ciò è garantito dal fatto che la trasmissione dei dati operata da Node.js avviene ottimizzando la trasmissione lungo la connessione HTTP. Se proviamo ad esaminare il risultato di una pagina prodotta con Node.js troveremo i seguenti headers

 

Connection: keep-alive

Transfer-Encoding: chunked

 

Il primo ci dice che la connessione tra client e server viene tenuta “viva”, cioè viene utilizzata più volte nell’ambito della stessa comunicazione tra server e client. Ciò migliora le performance e la velocità di trasmissione, perché riduce il numero di connessioni che dobbiamo aprire tra client e server. Il secondo header ci dice che i pacchetti vengono trasmessi in modo simile al concetto di streaming, cioè vengono inviati al client man mano che sono disponibili. Questo è l’opposto del concetto di buffering, che invece tende ad accumulare sul server tutti i dati, prima di trasmetterli al client. L’uso combinato di questi due header indica che Node.js è ottimizzato proprio per trasmettere e scambiare dati in modo efficiente. Tale ottimizzazione è del tutto trasparente al programmatore, che deve solamente occuparsi di scrivere il codice JavaScript. Chi volesse approfondire l’argomento può dare un’occhiata qui.

Comunità di sviluppatori

Ultimo (ma non ultimo) vantaggio di cui vogliamo parlare riguarda la vasta comunità di sviluppatori nata attorno a Node.js. Sul Web troviamo migliaia di applicazioni, esempi, snippet già pronti all’uso. Ad esempio, se usiamo Github, possiamo dare un’occhiata qui.

Navigando sul Web si possono trovare numerose applicazioni realizzate con Node. Js, tra cui software orientati al groupware ma anche videogiochi multiutente. Node.js rende molto facile gestire lo scambio centralizzato (sul server) da parte di numerose applicazioni client, come ad esempio una chat. È ben noto che quando il successo di una tecnologia è collegato alla nascita di comunità specializzate si crea una specie di feedback positivo, cioè una reazione a catena che aumenta sempre più la popolarità della tecnologia. Ogni giorno sempre più sviluppatori scelgono Node.js, e questo aumenta il numero di librerie, plugin, snippet e risorse che possiamo trovare sul Web. Per fare un esempio, anche se Node.js è molto giovane, sono già nati framework di sviluppo basati proprio su Node. Js, come ad esempio ExpressJS.

Chi ben comincia è a metà dell’opera, e Node.js è partito col piede giusto. Consigliamo di tenere d’occhio l’andamento di questa nuova tecnologia e fare qualche prova per saggiarne con mano sia la qualità, sia la semplicità di utilizzo.