La gestione delle eccezioni è l'elemento che distingue un software amatoriale da un'applicazione enterprise pronta per la produzione. Un errore non gestito correttamente può causare il crash improvviso di un server Node.js, rendere l'interfaccia utente (UI) inutilizzabile o, peggio ancora, corrompere i dati in modo silenzioso. Gestire gli errori significa prevedere l'imprevedibile, garantire un "fail gracefully" (fallimento controllato) e fornire feedback chiari sia agli utenti che agli sviluppatori.

Il blocco try/catch/finally

Il meccanismo fondamentale per intercettare le eccezioni in JavaScript è il costrutto try...catch. Il blocco finally è opzionale ma fondamentale per eseguire operazioni di pulizia, indipendentemente dall'esito dell'operazione.

Esempio Base di Gestione Eccezioni
try {
  // Tentativo di parsing di un input utente
  const dati = JSON.parse(inputUtente);
  elaboraDati(dati);
} catch (errore) {
  // Gestione dell'errore se qualcosa va storto
  console.error("Errore nel parsing dell'input:", errore.message);
  notificaUtente("I dati inseriti non sono validi. Riprova.");
} finally {
  // Viene eseguito sempre, utile per pulire risorse o chiudere caricamenti
  disabilitaSpinnerCaricamento();
}

Tipi di Errore Nativi in JavaScript

Riconoscere il tipo di errore è essenziale per decidere come rispondere. JavaScript offre diversi oggetti di errore predefiniti:

Eccezioni Comuni e Controllo del Tipo
// SyntaxError: codice o formato non valido
JSON.parse("{malformato}");

// TypeError: operazione su un tipo non previsto (es. null/undefined)
null.toString();
const lista = undefined;
lista.map(item => item);

// ReferenceError: variabile non dichiarata
console.log(variabileInesistente);

// RangeError: valore fuori dai limiti consentiti
new Array(-5);

// Come discriminare gli errori
try {
  operazioneRischiosa();
} catch (errore) {
  if (errore instanceof TypeError) {
    console.warn("Problema di tipo:", errore.message);
  } else if (errore instanceof SyntaxError) {
    console.error("Errore di sintassi nei dati.");
  } else {
    throw errore; // Rilancia l'errore se non sai come gestirlo
  }
}

Creare Classi di Errore Personalizzate

In applicazioni complesse, gli errori generici non bastano. Creare classi personalizzate che estendono Error permette di aggiungere metadati utili come codici di stato HTTP o identificatori univoci.

Custom Error Classes in ES6+
class AppError extends Error {
  constructor(messaggio, statusCode, codiceInterno) {
    super(messaggio);
    this.name = 'AppError';
    this.statusCode = statusCode;
    this.codiceInterno = codiceInterno;
    this.isOperational = true; // Indica che è un errore previsto (es. validazione)
  }
}

class NotFoundError extends AppError {
  constructor(risorsa = 'Risorsa') {
    super(`${risorsa} non trovata`, 404, 'NOT_FOUND');
    this.name = 'NotFoundError';
  }
}

// Esempio di utilizzo in una funzione
function recuperaUtente(id) {
  const utente = db.find(id);
  if (!utente) {
    throw new NotFoundError('Utente');
  }
  return utente;
}

Gestione degli Errori Asincroni

Con l'avvento di async/await, la gestione degli errori asincroni è diventata molto più simile a quella sincrona, ma richiede comunque attenzione particolare per evitare "unhandled rejections".

Async/Await e Try/Catch
// ✅ Approccio corretto
async function caricaProfilo(id) {
  try {
    const risposta = await fetch(`/api/user/${id}`);

    if (!risposta.ok) {
      throw new AppError(
        `Errore HTTP ${risposta.status}`,
        risposta.status,
        'FETCH_ERROR'
      );
    }

    return await risposta.json();
  } catch (errore) {
    // Gestione centralizzata o rilancio
    console.error("Errore durante il recupero del profilo:", errore);
    throw errore;
  }
}

// ❌ Errore comune: dimenticare di gestire la promessa
async function erroreGrave() {
  const dati = await fetch('/api/data'); // Se fallisce, l'errore si perde!
}

Handler Globali per Errori Non Intercettati

Nonostante i nostri sforzi, alcuni errori potrebbero sfuggire. È vitale impostare dei "paracadute" globali per loggare questi incidenti.

Intercettori Globali (Browser e Node.js)
// Lato Browser
window.addEventListener('error', (event) => {
  console.error('Errore globale catturato:', event.error);
  // Qui potresti inviare il log a un servizio esterno come Sentry
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Promessa non gestita:', event.reason);
});

// Lato Node.js
process.on('uncaughtException', (err) => {
  console.error('Eccezione non gestita:', err);
  process.exit(1); // Forza l'uscita per evitare stati instabili
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Rejection non gestita a:', promise, 'motivo:', reason);
});

Middleware di Errore in Express.js

Se sviluppi backend in Node.js, Express offre un pattern specifico per gestire gli errori in modo centralizzato tramite middleware a 4 parametri.

Middleware Centralizzato
app.use((err, req, res, next) => {
  const status = err.statusCode || 500;
  const msg = err.isOperational ? err.message : 'Errore interno del server';

  // Log dettagliato per lo sviluppatore
  console.error(`[${err.codiceInterno || 'GENERIC'}] ${err.stack}`);

  // Risposta pulita per il client
  res.status(status).json({
    success: false,
    error: {
      message: msg,
      code: err.codiceInterno || 'INTERNAL_ERROR',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});

Best Practice Fondamentali

  • Non "inghiottire" mai gli errori — Evita blocchi catch vuoti; nascondono bug difficili da trovare.
  • Distingui tra errori operativi e bug — Gli errori operativi (input errato, rete assente) vanno gestiti, i bug (undefined is not a function) vanno corretti.
  • Rilancia gli errori sconosciuti — Se un catch intercetta un errore che non si aspetta, usa throw per passarlo a un gestore superiore.
  • Logga con contesto — Non salvare solo il messaggio, includi ID utente, URL della richiesta e timestamp.
  • Usa strumenti di monitoraggio — In produzione, affida i log a servizi come Sentry, Datadog o LogRocket per alert in tempo reale.
  • UI Resiliente — Mostra sempre un messaggio amichevole all'utente invece di una pagina bianca o un caricamento infinito.

Ottimizza il tuo Codice JavaScript

Utilizza i nostri strumenti gratuiti per formattare e analizzare il tuo codice istantaneamente.