modulo 19 di 24 / Ore 41-60
Moduli 17-24 / Checkpoint 60 ore
Caricamento e salvataggio: costi e frequenza
Analisi del costo di caricamento e salvataggio in localStorage nel Registro del laboratorio scolastico: quando costa di più, con quale frequenza avviene, quando ottimizzare.
Caricamento e salvataggio: costi e frequenza
Questa lezione appartiene al Modulo 19 — Performance e ottimizzazione pratica.
Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. In questa lezione analizzi quanto costano le operazioni di lettura e scrittura su localStorage e decidi quando vale la pena ottimizzare.
Obiettivo della lezione
Capire il costo reale di caricaTutti e salva nel Registro. Misurare con console.time. Identificare le situazioni in cui si legge e si scrive troppo spesso e proporre una soluzione misurata.
Alla fine devi avere: un’analisi documentata con almeno tre misurazioni e una decisione motivata su cosa ottimizzare (o non ottimizzare).
Come funzionano caricaTutti e salva
L’adapter localStorage del Registro fa operazioni semplici ma costose al crescere dei dati:
// infrastructure/archivioStrumentiLocalStorage.js
export function caricaTutti() {
const raw = localStorage.getItem("registro_strumenti");
if (!raw) return [];
return JSON.parse(raw).map(dtoToStrumento);
}
export function salva(strumenti) {
const dtos = strumenti.map(strumentoToDTO);
localStorage.setItem("registro_strumenti", JSON.stringify(dtos));
}
Ogni caricaTutti legge l’intera stringa da localStorage e fa il parse di tutto l’array. Ogni salva serializza l’intero array e riscrive la stringa in localStorage. Non c’è scrittura parziale, non c’è aggiornamento incrementale.
Il costo in pratica
Per il Registro scolastico, con pochi decine di strumenti, il costo è trascurabile — pochi millisecondi. Ma capire il costo serve a ragionare su quando ottimizzare e quando la semplicità è preferibile alla performance.
I fattori che influenzano il costo sono:
- dimensione dell’array (numero di strumenti o prestiti)
- dimensione di ogni oggetto (quanti campi, quante stringhe lunghe)
- frequenza con cui si chiama
caricaTuttiosalvain una singola sessione utente
Il problema tipico del Registro non è un singolo salvataggio lento — è chiamare caricaTutti molte volte consecutive senza che i dati siano cambiati.
Nel Registro del laboratorio
Un pattern problematico comune è questo:
// PROBLEMA: caricaTutti chiamato tre volte per la stessa operazione
function mostraStatistiche(archivioStrumenti, archivioPrestiti) {
const tutti = archivioStrumenti.caricaTutti();
const disponibili = archivioStrumenti.caricaTutti().filter(s => s.stato === "disponibile");
const inPrestito = archivioStrumenti.caricaTutti().filter(s => s.stato === "in-prestito");
return { totale: tutti.length, disponibili: disponibili.length, inPrestito: inPrestito.length };
}
Si legge da localStorage tre volte quando basterebbe una. La soluzione non richiede cache o ottimizzazioni complesse — basta chiamare caricaTutti una sola volta:
// CORRETTO: una sola lettura
function mostraStatistiche(archivioStrumenti) {
const tutti = archivioStrumenti.caricaTutti();
const disponibili = tutti.filter(s => s.stato === "disponibile");
const inPrestito = tutti.filter(s => s.stato === "in-prestito");
return { totale: tutti.length, disponibili: disponibili.length, inPrestito: inPrestito.length };
}
Procedura guidata
- Identifica i punti del Registro dove si chiama
caricaTuttiosalva. - Conta quante volte vengono chiamati in sequenza nella stessa operazione.
- Aggiungi misurazioni con
console.timeeconsole.timeEnd. - Documenta i tempi misurati.
- Identifica le chiamate ridondanti e rimuovile.
- Misura di nuovo dopo la modifica e confronta.
Attività in classe
Compila questa scheda di analisi:
## Analisi costi localStorage
Operazione 1: caricaTutti in CercaStrumenti
Tempo misurato (console.time): __ ms
Chiamate ridondanti trovate: __
Operazione 2: salva in RegistraStrumento
Tempo misurato: __ ms
Necessaria ogni volta? sì / no — motivo:
Operazione 3: caricaTutti + salva in ApriPrestito
Tempo totale: __ ms
Cosa si legge, cosa si scrive:
Caso problematico trovato nel mio codice:
Soluzione applicata:
Spiegazione guidata per studiare
localStorage è sincrono e bloccante: mentre legge o scrive, il browser non fa altro. Per quantità piccole questo non si nota. Per array grandi (centinaia di record con campi lunghi) può causare un ritardo percepibile.
La regola pratica per il Registro è: non ottimizzare prima di misurare. Tante ottimizzazioni premature rendono il codice più complesso senza beneficio reale. Prima misura con console.time, poi decidi se il costo giustifica la complessità aggiunta.
La seconda regola: carica una volta, usa più volte. Se hai bisogno dei dati in più punti della stessa funzione, chiamala una volta e passa l’array come parametro. Non serve una cache — basta non chiamare caricaTutti più volte nella stessa esecuzione.
Passo per passo nel progetto
- Apri
src/application/e cerca tutti i punti dove si chiamaarchivio.caricaTutti(). - Per ogni caso d’uso, aggiungi misurazioni temporanee:
// application/registraStrumento.js — con misurazione temporanea
export function registraStrumento({ nome, categoria }, archivio) {
console.time("caricaTutti-registraStrumento");
const strumenti = archivio.caricaTutti();
console.timeEnd("caricaTutti-registraStrumento");
const nuovoStrumento = {
id: crypto.randomUUID(),
nome: nome.trim(),
categoria,
stato: "disponibile"
};
strumenti.push(nuovoStrumento);
console.time("salva-registraStrumento");
archivio.salva(strumenti);
console.timeEnd("salva-registraStrumento");
return nuovoStrumento;
}
- Apri la console del browser, esegui l’operazione e leggi i tempi.
- Cerca i pattern di lettura ripetuta. Un caso tipico nel Registro:
// PROBLEMA: apriPrestito legge gli strumenti due volte
export function apriPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti) {
const strumenti = archivioStrumenti.caricaTutti();
const strumento = strumenti.find(s => s.id === strumentoId);
if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
// secondo caricaTutti — ridondante!
const tuttiGliStrumenti = archivioStrumenti.caricaTutti();
tuttiGliStrumenti.find(s => s.id === strumentoId).stato = "in-prestito";
archivioStrumenti.salva(tuttiGliStrumenti);
}
Il secondo caricaTutti è ridondante — hai già l’array nella variabile strumenti. Correzione:
export function apriPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti) {
const strumenti = archivioStrumenti.caricaTutti(); // una sola lettura
const strumento = strumenti.find(s => s.id === strumentoId);
if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
if (strumento.stato !== "disponibile") throw new Error("STRUMENTO_NON_DISPONIBILE");
strumento.stato = "in-prestito"; // modifica sull'array già caricato
archivioStrumenti.salva(strumenti); // una sola scrittura
// ...
}
- Dopo le correzioni, misura di nuovo e documenta il risultato.
Esempio JS — frequenza tipica nel Registro
// Scenario tipico in una sessione di 10 minuti nel laboratorio:
// - 5 ricerche di strumenti → 5 caricaTutti (sola lettura)
// - 3 prestiti aperti → 3 coppie caricaTutti + salva
// - 2 restituzioni → 2 coppie caricaTutti + salva
// - 1 segnalazione guasto → 1 coppia caricaTutti + salva
// Totale: ~11 caricaTutti, ~6 salva
// Per 50 strumenti, ogni operazione dura < 1 ms → costo totale < 20 ms
// Non c'è nulla da ottimizzare in questo scenario
La conclusione pratica: per un registro scolastico con meno di 500 strumenti e pochi utenti contemporanei, non serve ottimizzare. Serve evitare le chiamate ridondanti evidenti.
Prova di comprensione
Rispondi per iscritto:
- Qual è la differenza tra “caricaTutti è lento” e “caricaTutti viene chiamato troppo spesso”?
- Perché non conviene fare una cache in memoria dei dati di localStorage nel Registro?
- Cosa misura
console.time/console.timeEnde cosa non misura? - Quando sarebbe ragionevole aggiungere una cache in memoria per gli strumenti?
Errori da evitare
- ottimizzare prima di misurare — il codice diventa complesso senza beneficio reale;
- chiamare
caricaTuttipiù volte nella stessa funzione senza necessità; - fare
salvadopo ogni singola modifica quando puoi raccogliere le modifiche e salvare una volta sola; - confondere la lentezza percepita con un problema di localStorage — spesso è la UI che non risponde durante operazioni sincrone.
Prodotto da consegnare
Una scheda di analisi compilata con i tempi misurati e almeno una correzione applicata al codice. Come verifica: mostra console.time in azione nel browser con i dati reali del Registro.
Checklist di chiusura
- ho misurato almeno tre operazioni con
console.time; - ho identificato almeno una chiamata ridondante a
caricaTutti; - ho corretto la chiamata ridondante e rimisurare;
- ho documentato il risultato nella scheda;
- le misurazioni temporanee sono state rimosse dal codice finale.
Risultato atteso
Ho misurato caricaTutti e salva nel mio Registro.
I tempi sono: [valori].
Ho trovato una chiamata ridondante in: [caso d'uso].
Dopo la correzione il costo è sceso da X a Y chiamate a localStorage.
Conclusione: con i dati attuali non serve ottimizzare ulteriormente.