Federico Lelli / SchoolNode.js & Express
LEZIONE 07 · Moduli e I/O

File system: sync e async

Prima di iniziare: le parole della lezione

File system, encoding e Buffer
Il file system organizza file e cartelle. L’encoding stabilisce come convertire byte in testo: UTF-8 è una scelta comune. Un Buffer contiene byte, non una stringa JavaScript.
Sincrono e asincrono
Una chiamata sincrona restituisce il risultato prima di proseguire e può bloccare il thread. Una chiamata asincrona avvia il lavoro e ne rende disponibile il risultato successivamente.
Promise, async e await
Una Promise rappresenta un esito futuro, riuscito o fallito. Una funzione async restituisce una Promise; await sospende quella funzione finché la Promise si risolve o fallisce.
try/catch
try delimita operazioni che possono fallire; catch riceve l’errore e permette di gestirlo.

fs consente di leggere e scrivere file. Le operazioni *Sync bloccano il thread JavaScript fino al termine; per server e attività I/O frequenti preferisci le API asincrone.

// file.cjs
const fs = require('node:fs');
fs.writeFileSync('nota.txt', 'Prima riga\n');
fs.appendFileSync('nota.txt', 'Seconda riga\n');
console.log(fs.readFileSync('nota.txt', 'utf8'));

Stessa operazione con Promise:

// file-async.mjs
import { readFile, writeFile, appendFile } from 'node:fs/promises';
try {
  await writeFile('nota.txt', 'Prima riga\n');
  await appendFile('nota.txt', 'Seconda riga\n');
  const testo = await readFile('nota.txt', 'utf8');
  console.log(testo);
} catch (errore) {
  console.error('Errore file:', errore.message);
}

Precisazione: writeFile sovrascrive per impostazione predefinita; appendFile aggiunge in fondo. Non mischiare letture e scritture concorrenti dello stesso file senza coordinarle.

Chi fa cosa, e perché

La versione a callback del corso segue la convenzione error-first: il primo parametro contiene l’eventuale errore, il secondo il risultato. Senza utf8, una lettura restituisce normalmente un Buffer. Se devi leggere due file e poi scrivere il contenuto combinato, devi aspettare entrambe le letture: posizionare la scrittura sotto le chiamate asincrone non basta.

Le Promise evitano di annidare molte callback. Due await consecutivi mantengono l’ordine delle operazioni; Promise.all può attendere più letture indipendenti. Non rende sicure scritture concorrenti sullo stesso file. Le API sincrone possono andare bene per piccoli script o preparazione iniziale; dentro una route rallentano anche gli altri utenti.

Un altro esempio guidato

// lettura.cjs — crea prima nota.txt nella cartella di avvio
const fs = require('node:fs');
console.log('1. Avvio lettura');
fs.readFile('nota.txt', 'utf8', (errore, testo) => {
  if (errore) {
    console.error('Lettura fallita:', errore.code);
    return;
  }
  console.log('3. Contenuto:', testo);
});
console.log('2. Il programma può proseguire');

Come leggerlo e risultato atteso. Le righe 1 e 2 vengono stampate prima della callback. Se il file manca, leggi tipicamente ENOENT, codice che segnala un percorso inesistente. return impedisce di usare testo dopo l’errore. Il percorso relativo viene risolto dalla directory di lavoro.

Prova tu. Rinomina il file di input e osserva il ramo di errore; poi ripristinalo. Verifica che la scrittura con writeFile sostituisca davvero il contenuto precedente prima di usarla su dati importanti.

Torna all’indice ↑

Materiali didattici curati e rielaborati da Federico Lelli. Le fonti e i riferimenti specifici sono indicati nei materiali disponibili.