Tag: nodejs, backend, javascript, event-loop, v8, async
Livello: intermedio
TL;DR: Runtime environment per eseguire JavaScript lato server, basato sul motore V8. Utilizza un modello I/O non bloccante e asincrono, ideale per applicazioni scalabili.
Architettura single-threaded con Event Loop che gestisce richieste concorrenti in modo asincrono.
Basato sul motore V8 di Google Chrome, compila JavaScript in codice macchina nativo.
Le operazioni di I/O non bloccanti (database, rete, file system) prevengono il blocco del thread principale delegandole a libuv.
La gestione dei moduli avviene storicamente tramite CommonJS (require()) e più recentemente con ES Modules (import).
Il file package.json definisce le dipendenze, gli script e i metadati del progetto (tramite npm, pnpm o yarn).
EventEmitter è il modulo core per implementare l'architettura event-driven (scatenare e ascoltare eventi custom).
Gli Stream (Readable, Writable, Duplex, Transform) gestiscono flussi di dati sequenziali, essenziali per manipolare file di grandi dimensioni con bassa memoria.
I Worker Threads permettono di eseguire calcoli CPU-intensive in parallelo senza bloccare l'Event Loop principale.
Q: How does Node.js handle concurrency despite being single-threaded?R: Sfrutto l'Event Loop e le API asincrone. Le operazioni pesanti di I/O vengono relegate al kernel del sistema operativo tramite libuv, lasciando libero il thread principale.
Q: What is the difference between process.nextTick() and setImmediate()?R: process.nextTick() ha la priorità massima e viene eseguito subito dopo l'operazione corrente, prima che l'Event Loop avanzi. setImmediate() si accoda per il prossimo ciclo dell'Event Loop.
Q: How do you avoid callback hell?R: Uso costantemente Promises e la sintassi async/await. Strutturo il codice in funzioni piccole e modulari per mantenere una gerarchia piatta e leggibile.
Q: What is the purpose of Buffer in Node.js?R: Lo uso per gestire direttamente i dati binari (raw memory allocation). È fondamentale quando si lavora con stream, file system o protocolli di rete TCP.
Bloccare l'Event Loop con operazioni sincrone (es. fs.readFileSync o algoritmi pesanti), abbattendo le performance generali.
Dimenticare di gestire le Promise respinte, scatenando unhandledRejection e rischiando crash inaspettati dell'app.
Memory leak derivanti da accumulo di event listener non rimossi (es. EventEmitter.on senza removeListener).
Affidarsi a variabili globali per lo stato dell'applicazione, causando collisioni e comportamenti anomali sotto alto traffico.
// Server HTTP base nativo
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ message: 'Hello from Node.js' }));
});
server.listen(3000, () => {
console.log('Server in ascolto sulla porta 3000');
});
JavaScript
// Utilizzo di fs.promises con async/await e catch
const fs = require('fs').promises;
async function readConfigFile() {
try {
const data = await fs.readFile('./config.json', 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('Errore lettura config:', error.message);
throw error;
}
}
Bash
# Inizializzare un progetto e installare pacchetti
npm init -y
npm install express dotenv
npm install --save-dev nodemon jest
[ ] Ordine esatto di esecuzione delle code nell'Event Loop (Timers, Pending, Idle, Poll, Check, Close).
[ ] Implementazione e differenze specifiche tra i vari tipi di Stream (Duplex vs Transform).
[ ] Profilazione della memoria e rilevamento dei memory leak in Node.js (V8 heap snapshot).