Quiz: Padronanza IO di NodeJS
Metti alla prova la tua conoscenza di file, stream e buffer
Pronto a tuffarti nel mondo di NodeJS I/O? 🌊
Questo quiz metterà alla prova la tua comprensione delle operazioni di I/O di Node, dalle operazioni di base del file system fino ai concetti avanzati di streaming. Tratteremo buffer, codifiche e le migliori pratiche per gestire i dati in modo efficiente.
Vediamo quanto conosci i tuoi stream rispetto ai buffer! 🚀
Cosa fa questo codice?
const buf = Buffer.alloc(5);console.log(buf);Buffer.alloc(size) crea un nuovo Buffer della dimensione specificata riempito di zeri.
L’output sarà: <Buffer 00 00 00 00 00>
Se vuoi creare un Buffer con dati casuali, usa Buffer.allocUnsafe(5).
Cosa stamperà?
const buf = Buffer.from([65]);console.log(buf.toString());I numeri nell’array rappresentano codici ASCII:
- 65: ‘A’
toString() converte questi byte nella loro rappresentazione stringa usando la codifica UTF-8 per impostazione predefinita.
Qual è l’ordine di output?
import fs from 'fs';fs.readFile('test.txt', 'utf8', (err, data) => { console.log(data);});console.log('Done');Poiché readFile è asincrono, il codice continua a eseguire mentre il file viene letto.
Pertanto, “Done” verrà stampato prima del contenuto del file.
Per attendere che il file sia letto prima, puoi usare la versione basata su Promise:
import { promises as fs } from 'fs';
async function read() { const data = await fs.readFile('test.txt', 'utf8'); console.log(data); console.log('Done');}Cosa restituisce fs.readFileSync() per impostazione predefinita?
import fs from 'fs';const content = fs.readFileSync('test.txt');fs.readFileSync() restituisce un Buffer per impostazione predefinita quando non viene specificata un’encoding. Se vuoi una stringa, devi:
- Specificare un’encoding:
fs.readFileSync('test.txt', 'utf8') - Convertire il Buffer:
content.toString()
Approfondisci fs.readFileSync nella documentazione di Node.js
Quale insieme di eventi è comunemente usato con gli stream Readable?
Gli stream Readable emettono diversi eventi importanti:
- ‘data’: Quando i dati sono disponibili per la lettura
- ‘end’: Quando non ci sono più dati da leggere
- ‘error’: Quando si verifica un errore
- ‘close’: Quando lo stream e la risorsa sottostante sono chiusi
const readable = fs.createReadStream('file.txt');readable.on('data', chunk => console.log(chunk));readable.on('end', () => console.log('Done!'));Cosa fa questo codice?
import fs from 'fs';const readable = fs.createReadStream('source.txt');const writable = fs.createWriteStream('dest.txt');readable.pipe(writable);pipe() collega un flusso leggibile a un flusso scrivibile, gestendo automaticamente il back‑pressure e copiando i dati a blocchi senza caricare l’intero file in memoria.
Questo è efficiente in termini di memoria per file di grandi dimensioni rispetto a fs.readFile() seguito da fs.writeFile().
Cosa fa l’opzione recursive?
import fs from 'fs';fs.mkdirSync('./a/b/c', { recursive: true });L’opzione recursive: true crea le directory padre se non esistono.
Senza questa opzione, provare a creare ’./a/b/c’ genererebbe un errore se ’./a’ o ’./a/b’ non esistono.
È simile al comando shell mkdir -p.
Cosa stamperà questo?
import { Transform } from 'stream';const upperCase = new Transform({ transform(chunk, encoding, callback) { callback(null, chunk.toString().toUpperCase()); }});process.stdin .pipe(upperCase) .pipe(process.stdout);// Input: "hello world"Le stream di trasformazione modificano i dati mentre li attraversano. Qui, ogni chunk è:
- Convertito in una stringa
- Trasformato in maiuscolo
- Inviato a stdout
Questo crea una pipeline che converte tutto l’input in maiuscolo.
Quante volte fs.watch() è garantito che venga attivato quando un file viene modificato?
import fs from 'fs';fs.watch('test.txt', (eventType, filename) => { console.log(`${filename} was changed`);});// Then modify test.txt oncefs.watch() non è garantito che venga attivato esattamente una volta per ogni modifica logica del file. Spesso si attiva più volte perché molti editor di testo:
- Salvano in un file temporaneo
- Lo rinominano nel file di destinazione
Per un monitoraggio più affidabile, considera di usare:
- Il pacchetto
chokidar - Il debounce del callback
- Usare
fs.watchFile()(anche se è meno efficiente)
Qual è l’output?
const buf1 = Buffer.from('Hello');const buf2 = Buffer.from('Hello');console.log(buf1 === buf2);I Buffer vengono confrontati per riferimento, non per valore. Anche se contengono gli stessi dati, sono oggetti diversi.
Per confrontare il contenuto dei Buffer, usa:
buf1.equals(buf2) // true// orBuffer.compare(buf1, buf2) === 0 // trueQual è lo scopo principale del backpressure nei flussi?
Il backpressure è un meccanismo che previene il sovraccarico di memoria mettendo in pausa la lettura quando la parte di scrittura non riesce a tenere il passo.
Esempio di backpressure manuale:
readable.on('data', (chunk) => { const canContinue = writable.write(chunk); if (!canContinue) { readable.pause(); writable.once('drain', () => readable.resume()); }});pipe() gestisce questo automaticamente!
Cosa fa questo codice?
import fs from 'fs';fs.symlinkSync('target.txt', 'link.txt');symlinkSync crea un collegamento simbolico (simile a un collegamento rapido) al file di destinazione.
Differenze chiave rispetto ai collegamenti fisso:
- Può collegare directory
- Può attraversare file system
- Si rompe se la destinazione viene eliminata
Per creare invece un collegamento fisso:
fs.linkSync('target.txt', 'hardlink.txt');In quali modalità possono operare gli stream di Node.js?
Gli stream possono operare in:
- Modalità binaria (predefinita): per buffer e stringhe
- Modalità oggetto: per qualsiasi valore JavaScript
Esempio di modalità oggetto:
import { Transform } from 'stream';const objectStream = new Transform({ objectMode: true, transform(chunk, encoding, callback) { callback(null, { value: chunk }); }});Qual è il tipo del parametro fd in questo callback?
import fs from 'fs';fs.open('test.txt', 'r', (err, fd) => { console.log(typeof fd);});I descrittori di file sono numeri che identificano in modo univoco i file aperti nel sistema operativo.
I primi tre descrittori di file sono riservati:
- 0: stdin
- 1: stdout
- 2: stderr
Ricorda sempre di chiudere i descrittori di file:
fs.close(fd, (err) => { if (err) throw err;});Quanti byte occuperà questa stringa in UTF-8?
const str = "Hello 🌍";const buf = Buffer.from(str);console.log(buf.length);In UTF-8:
- I caratteri ASCII (come ‘Hello ’) occupano 1 byte ciascuno
- L’emoji della Terra 🌍 occupa 4 byte
Quindi: 5 (Hello) + 1 (spazio) + 4 (🌍) = 10 byte
Per vedere i byte:
console.log(buf); // <Buffer 48 65 6c 6c 6f 20 f0 9f 8c 8d>Spero che ti sia divertito a mettere alla prova le tue conoscenze su Node JS I/O! Vuoi continuare? Dai un’occhiata alla mia Raccolta di Quiz per altri esercizi!