Node.js per sviluppatori PHP: il modello event driven
Single thread, non bloccante, EventEmitter: le differenze rispetto al modello sincrono di PHP
Chi scrive in PHP è abituato a un modello preciso: ogni richiesta HTTP viene gestita da un processo o da un thread dedicato, che esegue lo script dall'inizio alla fine e poi restituisce la risposta. Node.js, descritto dalla documentazione ufficiale come un runtime JavaScript asincrono e guidato dagli eventi, sceglie un approccio radicalmente diverso: tutto il codice applicativo gira su un unico thread.
PHP è come un cameriere che segue un cliente dall'ordine fino al conto, senza muoversi verso altri tavoli finché non ha finito. Node.js è invece un cameriere solo, ma che non aspetta mai fermo davanti alla cucina: prende l'ordine di un tavolo, lo passa in cucina, e mentre il piatto viene preparato va subito a servire un altro tavolo. Quando il piatto è pronto, torna a consegnarlo.
1. Un processo per richiesta: il modello di PHP
Nel classico setup PHP con Apache o PHP FPM, ogni richiesta in arrivo viene assegnata a un processo (o a un thread) del pool disponibile. Se quello script esegue una query lenta al database o legge un file di grandi dimensioni, quel processo resta fermo ad aspettare: durante l'attesa non fa altro, ma il server può comunque servire altre richieste assegnandole ad altri processi del pool.
2. Un solo thread, ma mai bloccato: il modello di Node.js
Node.js esegue il codice JavaScript dell'applicazione su un singolo thread. Le operazioni lente (leggere un file, interrogare un database, fare una richiesta di rete) non vengono eseguite direttamente su quel thread: vengono delegate al sistema sottostante, che avvisa Node quando il risultato è pronto. Nel frattempo, quell'unico thread resta libero di occuparsi di altre richieste, invece di restare fermo ad aspettare.
3. L'Event Loop: chi tiene tutto in ordine
A coordinare questo andirivieni di richieste in attesa è l'Event Loop: un ciclo continuo che controlla se qualche operazione in sospeso ha prodotto un risultato e, in quel caso, esegue la funzione associata. È lo stesso meccanismo che il browser usa per JavaScript, ma applicato qui a un ambiente server, dove le operazioni in attesa sono soprattutto file, database e connessioni di rete invece di clic ed eventi dell'interfaccia.
Fonte: la guida ufficiale «The Node.js Event Loop» ne descrive fasi e funzionamento in dettaglio.
4. EventEmitter: il cuore degli eventi in Node.js
Buona parte delle API di Node.js (dalle richieste HTTP ai flussi di dati) è costruita sopra la classe EventEmitter: un oggetto capace di emettere eventi con nome, ai quali si possono agganciare funzioni di ascolto con il metodo on(). Quando l'evento viene emesso con emit(), tutte le funzioni agganciate vengono eseguite, nell'ordine in cui sono state registrate.
const EventEmitter = require('events');
class Ordine extends EventEmitter {}
const ordine = new Ordine();
ordine.on('completato', (totale) => {
console.log(`Ordine completato, totale: ${totale} euro`);
});
ordine.emit('completato', 42);
5. Un esempio pratico: leggere un file senza fermare tutto
Il modulo integrato fs (file system) offre readFile() in versione non bloccante: la lettura del file parte, ma il resto dello script continua subito, senza aspettare. Il contenuto del file arriva più tardi, dentro la funzione di callback passata come ultimo argomento.
const fs = require('fs');
console.log('Inizio lettura del file...');
fs.readFile('dati.txt', 'utf8', (errore, contenuto) => {
if (errore) {
console.error('Errore nella lettura:', errore.message);
return;
}
console.log('Contenuto del file:', contenuto);
});
console.log(
'Questa riga viene stampata subito, prima del contenuto del file.'
);
L'ordine con cui compaiono i messaggi in console non è quello in cui sono scritti nel codice: «Inizio lettura del file...» e «Questa riga viene stampata subito» compaiono per primi, e solo dopo, quando il file è stato letto per intero, compare «Contenuto del file». In PHP, uno script equivalente scritto con file_get_contents() si fermerebbe ad aspettare il contenuto del file prima di proseguire con la riga successiva.
In sintesi
PHP assegna ogni richiesta a un processo dedicato che esegue tutto in sequenza, fermandosi ad aspettare quando incontra un'operazione lenta. Node.js esegue invece l'applicazione su un singolo thread e delega le operazioni lente al sistema, restando libero di servire altre richieste nel frattempo grazie all'Event Loop. La classe EventEmitter è lo strumento con cui molte API di Node.js, e molto codice applicativo, comunicano che un evento è avvenuto. Nessuno dei due modelli è «sbagliato»: il primo è più semplice da seguire riga per riga, il secondo regge meglio un numero molto alto di connessioni contemporanee in attesa di I/O. Nella prossima pillola, dedicata al back-end, vedremo Express.js, il framework che semplifica la scrittura di applicazioni web sopra questo modello.
Commenti (0)
Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).
Nessun commento. Sii il primo a scrivere qualcosa.