DanLevy.net

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).

Approfondisci l’allocazione dei Buffer

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.

Scopri di più sulla codifica dei Buffer

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:

  1. Specificare un’encoding: fs.readFileSync('test.txt', 'utf8')
  2. 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!'));

Scopri di più sugli eventi Stream

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().

Scopri di più su pipe()

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.

Scopri di più su mkdir

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 è:

  1. Convertito in una stringa
  2. Trasformato in maiuscolo
  3. Inviato a stdout

Questo crea una pipeline che converte tutto l’input in maiuscolo.

Scopri di più sui Transform streams

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 once

fs.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:

  1. Salvano in un file temporaneo
  2. 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)

Scopri di più su fs.watch()

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
// or
Buffer.compare(buf1, buf2) === 0 // true

Scopri di più sul confronto dei Buffer

Qual è 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!

Scopri di più sul backpressure

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');

Scopri di più sui collegamenti simbolici

In quali modalità possono operare gli stream di Node.js?

Gli stream possono operare in:

  1. Modalità binaria (predefinita): per buffer e stringhe
  2. 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 });
}
});

Scopri di più sulle modalità stream

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;
});

Scopri di più sui descrittori di file

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>

Maggiori informazioni sulla codifica UTF-8

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!