Una guida definitiva alla gestione degli errori in JavaScript
Pubblicato: 2022-01-24La legge di Murphy afferma che tutto ciò che può andare storto alla fine andrà storto. Questo vale un po' troppo bene nel mondo della programmazione. Se crei un'applicazione, è probabile che creerai bug e altri problemi. Gli errori in JavaScript sono uno di questi problemi comuni!
Il successo di un prodotto software dipende dalla capacità dei suoi creatori di risolvere questi problemi prima di danneggiare i propri utenti. E JavaScript, tra tutti i linguaggi di programmazione, è noto per il suo design medio di gestione degli errori.
Se stai creando un'applicazione JavaScript, c'è un'alta probabilità che prima o poi sbaglierai con i tipi di dati. In caso contrario, potresti finire per sostituire un non definito con un operatore nullo o triplo uguale ( ===
) con un operatore doppio uguale ( ==
).
È solo umano commettere errori. Questo è il motivo per cui ti mostreremo tutto ciò che devi sapere sulla gestione degli errori in JavaScript.
Questo articolo ti guiderà attraverso gli errori di base in JavaScript e spiegherà i vari errori che potresti riscontrare. Imparerai quindi come identificare e correggere questi errori. Ci sono anche un paio di suggerimenti per gestire gli errori in modo efficace negli ambienti di produzione.
Senza ulteriori indugi, iniziamo!
Cosa sono gli errori JavaScript?
Gli errori di programmazione si riferiscono a situazioni che non consentono a un programma di funzionare normalmente. Può succedere quando un programma non sa come gestire il lavoro in corso, ad esempio quando si tenta di aprire un file inesistente o quando si raggiunge un endpoint API basato sul Web in assenza di connettività di rete.
Queste situazioni spingono il programma a lanciare errori all'utente, affermando che non sa come procedere. Il programma raccoglie quante più informazioni possibili sull'errore e quindi segnala che non può andare avanti.
I programmatori intelligenti cercano di prevedere e coprire questi scenari in modo che l'utente non debba capire un messaggio di errore tecnico come "404" in modo indipendente. Al contrario, mostrano un messaggio molto più comprensibile: "Impossibile trovare la pagina".
Gli errori in JavaScript sono oggetti mostrati ogni volta che si verifica un errore di programmazione. Questi oggetti contengono ampie informazioni sul tipo di errore, l'istruzione che ha causato l'errore e l'analisi dello stack quando si è verificato l'errore. JavaScript consente inoltre ai programmatori di creare errori personalizzati per fornire informazioni aggiuntive durante il debug dei problemi.
Proprietà di un errore
Ora che la definizione di un errore JavaScript è chiara, è il momento di immergersi nei dettagli.
Gli errori in JavaScript contengono determinate proprietà standard e personalizzate che aiutano a comprendere la causa e gli effetti dell'errore. Per impostazione predefinita, gli errori in JavaScript contengono tre proprietà:
- messaggio : un valore stringa che contiene il messaggio di errore
- name : il tipo di errore che si è verificato (ne parleremo più approfonditamente nella prossima sezione)
- stack : la traccia dello stack del codice eseguita quando si è verificato l'errore.
Inoltre, gli errori possono anche contenere proprietà come columnNumber, lineNumber, fileName e così via, per descrivere meglio l'errore. Tuttavia, queste proprietà non sono standard e possono essere presenti o meno in ogni oggetto di errore generato dall'applicazione JavaScript.
Comprensione della traccia dello stack
Una traccia dello stack è l'elenco delle chiamate al metodo in cui si trovava un programma quando si verifica un evento come un'eccezione o un avviso. Ecco come appare una traccia dello stack di esempio accompagnata da un'eccezione:

Come puoi vedere, inizia stampando il nome dell'errore e il messaggio, seguiti da un elenco di metodi che venivano chiamati. Ogni chiamata al metodo indica la posizione del codice sorgente e la riga in cui è stata richiamata. Puoi utilizzare questi dati per navigare nella tua codebase e identificare quale parte di codice sta causando l'errore.
Questo elenco di metodi è organizzato in modo impilato. Mostra dove è stata generata per la prima volta l'eccezione e come si è propagata attraverso le chiamate al metodo in pila. L'implementazione di un catch per l'eccezione non consentirà che si propaghi attraverso lo stack e arresti il programma in modo anomalo. Tuttavia, potresti voler non rilevare errori irreversibili per arrestare intenzionalmente il programma in alcuni scenari.
Errori vs eccezioni
La maggior parte delle persone di solito considera gli errori e le eccezioni come la stessa cosa. Tuttavia, è essenziale notare una leggera ma fondamentale differenza tra loro.
Per capirlo meglio, facciamo un rapido esempio. Ecco come puoi definire un errore in JavaScript:
const wrongTypeError = TypeError("Wrong type found, expected character")
Ed è così che l'oggetto wrongTypeError
diventa un'eccezione:
throw wrongTypeError
Tuttavia, la maggior parte delle persone tende a utilizzare la forma abbreviata che definisce gli oggetti di errore mentre li lancia:
throw TypeError("Wrong type found, expected character")
Questa è una pratica standard. Tuttavia, è uno dei motivi per cui gli sviluppatori tendono a confondere eccezioni ed errori. Pertanto, conoscere i fondamenti è fondamentale anche se usi le abbreviazioni per portare a termine il tuo lavoro rapidamente.
Tipi di errori in JavaScript
Esiste una gamma di tipi di errore predefiniti in JavaScript. Vengono scelti e definiti automaticamente dal runtime JavaScript ogni volta che il programmatore non gestisce esplicitamente gli errori nell'applicazione.
Questa sezione ti guiderà attraverso alcuni dei tipi più comuni di errori in JavaScript e capirà quando e perché si verificano.
Errore di intervallo
Viene generato un RangeError quando una variabile viene impostata con un valore al di fuori dell'intervallo di valori consentiti. Di solito si verifica quando si passa un valore come argomento a una funzione e il valore dato non si trova nell'intervallo dei parametri della funzione. A volte può diventare difficile da risolvere quando si utilizzano librerie di terze parti scarsamente documentate poiché è necessario conoscere l'intervallo di valori possibili affinché gli argomenti trasmettano il valore corretto.
Alcuni degli scenari comuni in cui si verifica RangeError sono:
- Tentativo di creare una matrice di lunghezze non valide tramite il costruttore Array.
- Passare valori non validi a metodi numerici come
toExponential()
,toPrecision()
,toFixed()
, ecc. - Passare valori illegali a funzioni di stringa come
normalize()
.
Errore di riferimento
Un ReferenceError si verifica quando qualcosa non va nel riferimento di una variabile nel codice. Potresti esserti dimenticato di definire un valore per la variabile prima di usarla, oppure potresti provare a usare una variabile inaccessibile nel tuo codice. In ogni caso, l'analisi dello stack fornisce ampie informazioni per trovare e correggere il riferimento della variabile che è in errore.
Alcuni dei motivi comuni per cui si verificano ReferenceError sono:
- Fare un errore di battitura in un nome di variabile.
- Tentativo di accedere a variabili con ambito di blocco al di fuori dei rispettivi ambiti.
- Fare riferimento a una variabile globale da una libreria esterna (come $ da jQuery) prima che venga caricata.
Errore di sintassi
Questi errori sono uno dei più semplici da correggere poiché indicano un errore nella sintassi del codice. Poiché JavaScript è un linguaggio di scripting interpretato anziché compilato, questi vengono generati quando l'app esegue lo script che contiene l'errore. Nel caso di linguaggi compilati, tali errori vengono identificati durante la compilazione. Pertanto, i binari dell'app non vengono creati finché non vengono corretti.
Alcuni dei motivi comuni per cui potrebbero verificarsi SyntaxError sono:
- Mancano le virgolette
- Mancano le parentesi di chiusura
- Allineamento improprio di parentesi graffe o altri caratteri
È buona norma utilizzare uno strumento linting nel tuo IDE per identificare tali errori prima che colpiscano il browser.
Digitare Errore
TypeError è uno degli errori più comuni nelle app JavaScript. Questo errore viene creato quando un valore non risulta essere di un particolare tipo previsto. Alcuni dei casi comuni in cui si verifica sono:
- Invocare oggetti che non sono metodi.
- Tentativo di accesso alle proprietà di oggetti null o non definiti
- Trattare una stringa come un numero o viceversa
Ci sono molte più possibilità in cui può verificarsi un TypeError. Vedremo alcuni casi famosi in seguito e impareremo come risolverli.
Errore interno
Il tipo InternalError viene utilizzato quando si verifica un'eccezione nel motore di runtime JavaScript. Potrebbe o meno indicare un problema con il tuo codice.
Il più delle volte, InternalError si verifica solo in due scenari:
- Quando una patch o un aggiornamento del runtime JavaScript contiene un bug che genera eccezioni (questo accade molto raramente)
- Quando il tuo codice contiene entità troppo grandi per il motore JavaScript (ad es. troppi switch case, inizializzatori di array troppo grandi, troppa ricorsione)
L'approccio più appropriato per risolvere questo errore consiste nell'identificare la causa tramite il messaggio di errore e ristrutturare la logica dell'app, se possibile, per eliminare l'improvviso picco di carico di lavoro sul motore JavaScript.
Errore URI
URIError si verifica quando una funzione di gestione URI globale come decodeURIComponent
viene utilizzata illegalmente. Di solito indica che il parametro passato alla chiamata al metodo non è conforme agli standard URI e quindi non è stato analizzato correttamente dal metodo.
La diagnosi di questi errori è generalmente facile poiché è sufficiente esaminare gli argomenti per la malformazione.
Errore di valutazione
Un EvalError si verifica quando si verifica un errore con una chiamata di funzione eval()
. La eval()
viene utilizzata per eseguire il codice JavaScript memorizzato nelle stringhe. Tuttavia, poiché l'utilizzo della eval()
è altamente sconsigliato a causa di problemi di sicurezza e le attuali specifiche ECMAScript non generano più la classe EvalError
, questo tipo di errore esiste semplicemente per mantenere la compatibilità con le versioni precedenti del codice JavaScript legacy.
Se stai lavorando su una versione precedente di JavaScript, potresti riscontrare questo errore. In ogni caso, è meglio esaminare il codice eseguito nella chiamata della funzione eval()
per eventuali eccezioni.
Creazione di tipi di errore personalizzati
Sebbene JavaScript offra un elenco adeguato di classi di tipi di errore da coprire per la maggior parte degli scenari, puoi sempre creare un nuovo tipo di errore se l'elenco non soddisfa i tuoi requisiti. Il fondamento di questa flessibilità risiede nel fatto che JavaScript ti consente di lanciare qualsiasi cosa letteralmente con il comando throw
.
Quindi, tecnicamente, queste affermazioni sono del tutto legali:
throw 8 throw "An error occurred"
Tuttavia, l'emissione di un tipo di dati primitivo non fornisce dettagli sull'errore, ad esempio il tipo, il nome o la traccia dello stack di accompagnamento. Per risolvere questo problema e standardizzare il processo di gestione degli errori, è stata fornita la classe Error
. È inoltre sconsigliato utilizzare tipi di dati primitivi durante la generazione di eccezioni.
Puoi estendere la classe Error
per creare la tua classe di errore personalizzata. Ecco un esempio di base di come puoi farlo:
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }
E puoi usarlo nel modo seguente:
throw ValidationError("Property not found: name")
E puoi quindi identificarlo usando la parola chiave instanceof
:
try { validateForm() // code that throws a ValidationError } catch (e) { if (e instanceof ValidationError) // do something else // do something else }
I 10 errori più comuni in JavaScript
Ora che conosci i tipi di errore comuni e come crearne di personalizzati, è il momento di esaminare alcuni degli errori più comuni che incontrerai durante la scrittura di codice JavaScript.
1. Errore di intervallo non rilevato
Questo errore si verifica in Google Chrome in diversi scenari. Innanzitutto, può succedere se chiami una funzione ricorsiva e non termina. Puoi verificarlo tu stesso nella Console per gli sviluppatori di Chrome:

Quindi, per risolvere un tale errore, assicurati di definire correttamente i casi limite della tua funzione ricorsiva. Un altro motivo per cui si verifica questo errore è se è stato passato un valore che non rientra nell'intervallo di un parametro di una funzione. Ecco un esempio:

Il messaggio di errore di solito indicherà cosa c'è di sbagliato nel tuo codice. Una volta apportate le modifiche, verrà risolto.

2. TypeError non rilevato: impossibile impostare la proprietà
Questo errore si verifica quando si imposta una proprietà su un riferimento non definito. Puoi riprodurre il problema con questo codice:
var list list.count = 0
Ecco l'output che riceverai:

Per correggere questo errore, inizializzare il riferimento con un valore prima di accedere alle sue proprietà. Ecco come appare una volta riparato:

3. TypeError non rilevato: impossibile leggere la proprietà
Questo è uno degli errori più frequenti in JavaScript. Questo errore si verifica quando si tenta di leggere una proprietà o di chiamare una funzione su un oggetto non definito. Puoi riprodurlo molto facilmente eseguendo il codice seguente in una Console per sviluppatori di Chrome:
var func func.call()
Ecco l'output:

Un oggetto non definito è una delle tante possibili cause di questo errore. Un'altra causa importante di questo problema può essere un'inizializzazione impropria dello stato durante il rendering dell'interfaccia utente. Ecco un esempio del mondo reale da un'applicazione React:
import React, { useState, useEffect } from "react"; const CardsList = () => { const [state, setState] = useState(); useEffect(() => { setTimeout(() => setState({ items: ["Card 1", "Card 2"] }), 2000); }, []); return ( <> {state.items.map((item) => ( <li key={item}>{item}</li> ))} </> ); }; export default CardsList;
L'app si avvia con un contenitore di stato vuoto e viene fornita con alcuni elementi dopo un ritardo di 2 secondi. Il ritardo viene messo in atto per imitare una chiamata di rete. Anche se la tua rete è super veloce, dovrai comunque affrontare un leggero ritardo a causa del quale il componente eseguirà il rendering almeno una volta. Se provi a eseguire questa app, riceverai il seguente errore:

Questo perché, al momento del rendering, il contenitore di stato non è definito; quindi, non esistono items
di proprietà su di esso. Risolvere questo errore è facile. Devi solo fornire un valore predefinito iniziale al contenitore di stato.
// ... const [state, setState] = useState({items: []}); // ...
Ora, dopo il ritardo impostato, la tua app mostrerà un output simile:

La correzione esatta nel codice potrebbe essere diversa, ma l'essenza qui è inizializzare sempre le variabili correttamente prima di usarle.
4. TypeError: 'undefined' non è un oggetto
Questo errore si verifica in Safari quando tenti di accedere alle proprietà o di chiamare un metodo su un oggetto non definito. Puoi eseguire lo stesso codice dall'alto per riprodurre tu stesso l'errore.

Anche la soluzione a questo errore è la stessa: assicurati di aver inizializzato correttamente le tue variabili e che non siano indefinite quando si accede a una proprietà o a un metodo.
5. TypeError: null non è un oggetto
Questo è, ancora una volta, simile all'errore precedente. Si verifica su Safari e l'unica differenza tra i due errori è che questo viene generato quando l'oggetto a cui si accede alla proprietà o al metodo è null
anziché undefined
. Puoi riprodurlo eseguendo il seguente pezzo di codice:
var func = null func.call()
Ecco l'output che riceverai:

Poiché null
è un valore impostato in modo esplicito su una variabile e non assegnato automaticamente da JavaScript. Questo errore può verificarsi solo se stai tentando di accedere a una variabile impostata su null
da solo. Quindi, devi rivedere il tuo codice e verificare se la logica che hai scritto è corretta o meno.
6. TypeError: impossibile leggere la proprietà 'length'
Questo errore si verifica in Chrome quando tenti di leggere la lunghezza di un oggetto null
o undefined
. La causa di questo problema è simile ai problemi precedenti, ma si verifica abbastanza frequentemente durante la gestione degli elenchi; quindi merita una menzione speciale. Ecco come riprodurre il problema:

Tuttavia, nelle versioni più recenti di Chrome, questo errore viene segnalato come Uncaught TypeError: Cannot read properties of undefined
. Ecco come appare ora:

La soluzione, ancora una volta, è garantire che l'oggetto a cui stai tentando di accedere esista e non sia impostato su null
.
7. TypeError: 'undefined' non è una funzione
Questo errore si verifica quando si tenta di richiamare un metodo che non esiste nello script oppure esiste ma non è possibile fare riferimento nel contesto di chiamata. Questo errore di solito si verifica in Google Chrome e puoi risolverlo controllando la riga di codice che genera l'errore. Se trovi un errore di battitura, correggilo e controlla se risolve il tuo problema.
Se hai utilizzato la parola chiave autoreferenziale this
nel tuo codice, questo errore potrebbe verificarsi se this
è legato in modo appropriato al tuo contesto. Considera il seguente codice:
function showAlert() { alert("message here") } document.addEventListener("click", () => { this.showAlert(); })
Se esegui il codice sopra, genererà l'errore di cui abbiamo discusso. Succede perché la funzione anonima passata come listener di eventi viene eseguita nel contesto del document
.
Al contrario, la funzione showAlert
è definita nel contesto della window
.
Per risolvere questo problema, devi passare il riferimento corretto alla funzione legandolo con il metodo bind()
:
document.addEventListener("click", this.showAlert.bind(this))
8. ReferenceError: l'evento non è definito
Questo errore si verifica quando si tenta di accedere a un riferimento non definito nell'ambito della chiamata. Questo di solito accade durante la gestione degli eventi poiché spesso forniscono un riferimento chiamato event
nella funzione di callback. Questo errore può verificarsi se si dimentica di definire l'argomento dell'evento nei parametri della funzione o se lo si scrive in modo errato.
Questo errore potrebbe non verificarsi in Internet Explorer o Google Chrome (poiché IE offre una variabile evento globale e Chrome allega automaticamente la variabile evento al gestore), ma può verificarsi in Firefox. Quindi è consigliabile tenere d'occhio questi piccoli errori.
9. TypeError: assegnazione a una variabile costante
Questo è un errore che nasce dalla negligenza. Se provi ad assegnare un nuovo valore a una variabile costante, incontrerai un risultato del genere:

Anche se sembra facile da risolvere in questo momento, immagina centinaia di tali dichiarazioni di variabili e una di esse definita erroneamente come const
invece di let
! A differenza di altri linguaggi di scripting come PHP, c'è una differenza minima tra lo stile di dichiarazione di costanti e variabili in JavaScript. Pertanto è consigliabile controllare prima di tutto le tue dichiarazioni quando incontri questo errore. Potresti anche imbatterti in questo errore se dimentichi che il suddetto riferimento è una costante e lo usi come variabile. Ciò indica negligenza o un difetto nella logica dell'app. Assicurati di controllarlo quando provi a risolvere questo problema.
10. (sconosciuto): errore di script
Si verifica un errore di script quando uno script di terze parti invia un errore al tuo browser. Questo errore è seguito da (sconosciuto) perché lo script di terze parti appartiene a un dominio diverso dalla tua app. Il browser nasconde altri dettagli per impedire la fuga di informazioni sensibili dallo script di terze parti.
Non è possibile risolvere questo errore senza conoscere i dettagli completi. Ecco cosa puoi fare per ottenere maggiori informazioni sull'errore:
- Aggiungi l'attributo
crossorigin
nel tag di script. - Impostare l'intestazione
Access-Control-Allow-Origin
corretta sul server che ospita lo script. - [Facoltativo] Se non hai accesso al server che ospita lo script, puoi considerare l'utilizzo di un proxy per inoltrare la tua richiesta al server e tornare al client con le intestazioni corrette.
Una volta che puoi accedere ai dettagli dell'errore, puoi quindi impostare la risoluzione del problema, che probabilmente riguarderà la libreria di terze parti o la rete.
Come identificare e prevenire gli errori in JavaScript
Sebbene gli errori discussi sopra siano i più comuni e frequenti in JavaScript, ti imbatterai in alcuni esempi che non possono mai essere sufficienti. È fondamentale capire come rilevare e prevenire qualsiasi tipo di errore in un'applicazione JavaScript durante lo sviluppo. Ecco come puoi gestire gli errori in JavaScript.
Errori di lancio e cattura manualmente
Il modo più fondamentale per gestire gli errori che sono stati generati manualmente o dal runtime è catturarli. Come la maggior parte delle altre lingue, JavaScript offre una serie di parole chiave per gestire gli errori. È essenziale conoscerli in modo approfondito prima di impostare la gestione degli errori nell'app JavaScript.
gettare
La prima e più basilare parola chiave del set è throw
. Come evidente, la parola chiave throw viene utilizzata per generare errori per creare manualmente eccezioni nel runtime JavaScript. Ne abbiamo già discusso in precedenza nel pezzo, ed ecco l'essenza del significato di questa parola chiave:
- Puoi
throw
qualsiasi cosa, inclusi numeri, stringhe e oggettiError
. - Tuttavia, non è consigliabile generare tipi di dati primitivi come stringhe e numeri poiché non contengono informazioni di debug sugli errori.
- Esempio:
throw TypeError("Please provide a string")
tentativo
La parola chiave try
viene utilizzata per indicare che un blocco di codice potrebbe generare un'eccezione. La sua sintassi è:
try { // error-prone code here }
È importante notare che un blocco catch
deve sempre seguire il blocco try
per gestire gli errori in modo efficace.
presa
La parola chiave catch
viene utilizzata per creare un blocco catch. Questo blocco di codice è responsabile della gestione degli errori rilevati dal blocco di try
finale. Ecco la sua sintassi:
catch (exception) { // code to handle the exception here }
Ed ecco come implementare insieme i blocchi try
e catch
:
try { // business logic code } catch (exception) { // error handling code }
A differenza di C++ o Java, non è possibile aggiungere più blocchi catch
a un blocco try
in JavaScript. Ciò significa che non puoi farlo:
try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } } catch (exception) { if (exception instanceof RangeError) { // do something } }
È invece possibile utilizzare un'istruzione if...else
o un'istruzione switch case all'interno del blocco catch singolo per gestire tutti i possibili casi di errore. Sembrerebbe così:
try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } else if (exception instanceof RangeError) { // do something else } }
finalmente
La parola chiave finally
viene utilizzata per definire un blocco di codice che viene eseguito dopo che un errore è stato gestito. Questo blocco viene eseguito dopo i blocchi try e catch.
Inoltre, il blocco finally verrà eseguito indipendentemente dal risultato degli altri due blocchi. Ciò significa che anche se il blocco catch non è in grado di gestire completamente l'errore o viene generato un errore nel blocco catch, l'interprete eseguirà il codice nel blocco finally prima che il programma si arresti in modo anomalo.
Per essere considerato valido, il blocco try in JavaScript deve essere seguito da un catch o da un finally block. Senza nessuno di questi, l'interprete solleverà un SyntaxError. Pertanto, assicurati di seguire i tuoi blocchi di prova con almeno uno di essi durante la gestione degli errori.
Gestisci gli errori a livello globale con il metodo onerror()
Il metodo onerror()
è disponibile per tutti gli elementi HTML per la gestione di eventuali errori che potrebbero verificarsi con essi. Ad esempio, se un tag img
non riesce a trovare l'immagine il cui URL è specificato, attiva il suo metodo onerror per consentire all'utente di gestire l'errore.
In genere, dovresti fornire un altro URL immagine nella chiamata di errore a cui ripiegare il tag img
. Ecco come puoi farlo tramite JavaScript:
const image = document.querySelector("img") image.onerror = (event) => { console.log("Error occurred: " + event) }
Tuttavia, puoi utilizzare questa funzione per creare un meccanismo globale di gestione degli errori per la tua app. Ecco come puoi farlo:
window.onerror = (event) => { console.log("Error occurred: " + event) }
Con questo gestore di eventi, puoi sbarazzarti dei blocchi multipli try...catch
che si trovano nel codice e centralizzare la gestione degli errori dell'app in modo simile alla gestione degli eventi. È possibile allegare più gestori di errori alla finestra per mantenere il principio di responsabilità singola dai principi di progettazione SOLID. L'interprete scorrerà tutti i gestori finché non raggiunge quello appropriato.
Passa gli errori tramite le richiamate
Mentre le funzioni semplici e lineari consentono alla gestione degli errori di rimanere semplice, i callback possono complicare la faccenda.
Considera il seguente pezzo di codice:
Hai bisogno di una soluzione di hosting che ti dia un vantaggio competitivo? Kinsta ti copre con velocità incredibile, sicurezza all'avanguardia e scalabilità automatica. Dai un'occhiata ai nostri piani
const calculateCube = (number, callback) => { setTimeout(() => { const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) calculateCube(4, callback)
La funzione precedente mostra una condizione asincrona in cui una funzione impiega del tempo per elaborare le operazioni e restituisce il risultato in un secondo momento con l'aiuto di un callback.
Se provi a inserire una stringa invece di 4 nella chiamata di funzione, otterrai NaN
come risultato.
Questo deve essere gestito correttamente. Ecco come:
const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") throw new Error("Numeric argument is expected") const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) try { calculateCube(4, callback) } catch (e) { console.log(e) }
Questo dovrebbe risolvere il problema idealmente. Tuttavia, se provi a passare una stringa alla chiamata di funzione, riceverai questo:

Anche se hai implementato un blocco try-catch durante la chiamata della funzione, dice ancora che l'errore non è stato rilevato. L'errore viene generato dopo l'esecuzione del blocco catch a causa del ritardo di timeout.
Ciò può verificarsi rapidamente nelle chiamate di rete, dove si insinuano ritardi imprevisti. È necessario coprire questi casi durante lo sviluppo dell'app.
Ecco come puoi gestire correttamente gli errori nei callback:
const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") { callback(new TypeError("Numeric argument is expected")) return } const cube = number * number * number callback(null, cube) }, 2000) } const callback = (error, result) => { if (error !== null) { console.log(error) return } console.log(result) } try { calculateCube('hey', callback) } catch (e) { console.log(e) }
Ora, l'output sulla console sarà:

Ciò indica che l'errore è stato gestito in modo appropriato.
Gestisci gli errori nelle promesse
La maggior parte delle persone tende a preferire le promesse per la gestione delle attività asincrone. Le promesse hanno un altro vantaggio: una promessa rifiutata non interrompe il tuo script. Tuttavia, è comunque necessario implementare un blocco catch per gestire gli errori nelle promesse. Per capirlo meglio, riscriviamo la funzione calcolaCube calculateCube()
usando Promises:
const delay = ms => new Promise(res => setTimeout(res, ms)); const calculateCube = async (number) => { if (typeof number !== "number") throw Error("Numeric argument is expected") await delay(5000) const cube = number * number * number return cube } try { calculateCube(4).then(r => console.log(r)) } catch (e) { console.log(e) }
Il timeout del codice precedente è stato isolato nella funzione di delay
per la comprensione. Se provi a inserire una stringa invece di 4, l'output che otterrai sarà simile a questo:

Ancora una volta, ciò è dovuto alla Promise
che ha generato l'errore dopo che tutto il resto ha completato l'esecuzione. La soluzione a questo problema è semplice. Aggiungi semplicemente una chiamata catch()
alla catena di promesse in questo modo:
calculateCube("hey") .then(r => console.log(r)) .catch(e => console.log(e))
Ora l'output sarà:

Puoi osservare quanto sia facile gestire gli errori con le promesse. Inoltre, puoi concatenare un finally()
e la chiamata promise per aggiungere codice che verrà eseguito dopo che la gestione degli errori è stata completata.
In alternativa, puoi anche gestire gli errori nelle promesse usando la tradizionale tecnica try-catch-finally. Ecco come sarebbe la tua chiamata di promessa in quel caso:
try { let result = await calculateCube("hey") console.log(result) } catch (e) { console.log(e) } finally { console.log('Finally executed") }
Tuttavia, questo funziona solo all'interno di una funzione asincrona. Pertanto il modo più preferito per gestire gli errori nelle promesse è il chain catch
e, finally
, la chiamata della promessa.
throw/catch vs onerror() vs callback vs promesse: qual è il migliore?
Con quattro metodi a tua disposizione, devi sapere come scegliere il più appropriato in un dato caso d'uso. Ecco come puoi decidere tu stesso:
lanciare/prendere
Utilizzerai questo metodo per la maggior parte del tempo. Assicurati di implementare le condizioni per tutti i possibili errori all'interno del blocco catch e ricorda di includere un blocco finally se è necessario eseguire alcune routine di pulizia della memoria dopo il blocco try.
Tuttavia, troppi blocchi try/catch possono rendere difficile la manutenzione del codice. Se ti trovi in una situazione del genere, potresti voler gestire gli errori tramite il gestore globale o il metodo promise.
Quando si decide tra i blocchi try/catch asincroni e promise's catch()
, è consigliabile utilizzare i blocchi try/catch asincroni poiché renderanno il codice lineare e di facile debug.
un errore()
È meglio usare il metodo onerror()
quando sai che la tua app deve gestire molti errori e che possono essere ben sparsi in tutta la codebase. Il metodo onerror
ti consente di gestire gli errori come se fossero solo un altro evento gestito dalla tua applicazione. Puoi definire più gestori di errori e allegarli alla finestra della tua app nel rendering iniziale.
Tuttavia, devi anche ricordare che il metodo onerror()
può essere inutilmente impegnativo da impostare in progetti più piccoli con un ambito di errore minore. Se sei sicuro che la tua app non genererà troppi errori, il metodo tradizionale di lancio/cattura funzionerà meglio per te.
Richiami e promesse
La gestione degli errori nelle richiamate e nelle promesse è diversa a causa della progettazione e della struttura del codice. Tuttavia, se scegli tra questi due prima di aver scritto il codice, sarebbe meglio seguire le promesse.
Questo perché le promesse hanno un costrutto integrato per concatenare un catch()
e un finally()
per gestire facilmente gli errori. Questo metodo è più semplice e pulito rispetto alla definizione di argomenti aggiuntivi/riutilizzo di argomenti esistenti per gestire gli errori.
Keep Track of Changes With Git Repositories
Many errors often arise due to manual mistakes in the codebase. While developing or debugging your code, you might end up making unnecessary changes that may cause new errors to appear in your codebase. Automated testing is a great way to keep your code in check after every change. However, it can only tell you if something's wrong. If you don't take frequent backups of your code, you'll end up wasting time trying to fix a function or a script that was working just fine before.
This is where git plays its role. With a proper commit strategy, you can use your git history as a backup system to view your code as it evolved through the development. You can easily browse through your older commits and find out the version of the function working fine before but throwing errors after an unrelated change.
You can then restore the old code or compare the two versions to determine what went wrong. Modern web development tools like GitHub Desktop or GitKraken help you to visualize these changes side by side and figure out the mistakes quickly.
A habit that can help you make fewer errors is running code reviews whenever you make a significant change to your code. If you're working in a team, you can create a pull request and have a team member review it thoroughly. This will help you use a second pair of eyes to spot out any errors that might have slipped by you.
Best Practices for Handling Errors in JavaScript
The above-mentioned methods are adequate to help you design a robust error handling approach for your next JavaScript application. However, it would be best to keep a few things in mind while implementing them to get the best out of your error-proofing. Here are some tips to help you.
1. Use Custom Errors When Handling Operational Exceptions
We introduced custom errors early in this guide to give you an idea of how to customize the error handling to your application's unique case. It's advisable to use custom errors wherever possible instead of the generic Error
class as it provides more contextual information to the calling environment about the error.
On top of that, custom errors allow you to moderate how an error is displayed to the calling environment. This means that you can choose to hide specific details or display additional information about the error as and when you wish.
You can go so far as to format the error contents according to your needs. This gives you better control over how the error is interpreted and handled.
2. Do Not Swallow Any Exceptions
Even the most senior developers often make a rookie mistake — consuming exceptions levels deep down in their code.
You might come across situations where you have a piece of code that is optional to run. If it works, great; if it doesn't, you don't need to do anything about it.
In these cases, it's often tempting to put this code in a try block and attach an empty catch block to it. However, by doing this, you'll leave that piece of code open to causing any kind of error and getting away with it. This can become dangerous if you have a large codebase and many instances of such poor error management constructs.
The best way to handle exceptions is to determine a level on which all of them will be dealt and raise them until there. This level can be a controller (in an MVC architecture app) or a middleware (in a traditional server-oriented app).
This way, you'll get to know where you can find all the errors occurring in your app and choose how to resolve them, even if it means not doing anything about them.
3. Use a Centralized Strategy for Logs and Error Alerts
Logging an error is often an integral part of handling it. Those who fail to develop a centralized strategy for logging errors may miss out on valuable information about their app's usage.
An app's event logs can help you figure out crucial data about errors and help to debug them quickly. If you have proper alerting mechanisms set up in your app, you can know when an error occurs in your app before it reaches a large section of your user base.
It's advisable to use a pre-built logger or create one to suit your needs. You can configure this logger to handle errors based on their levels (warning, debug, info, etc.), and some loggers even go so far as to send logs to remote logging servers immediately. This way, you can watch how your application's logic performs with active users.
4. Notify Users About Errors Appropriately
Another good point to keep in mind while defining your error handling strategy is to keep the user in mind.
All errors that interfere with the normal functioning of your app must present a visible alert to the user to notify them that something went wrong so the user can try to work out a solution. If you know a quick fix for the error, such as retrying an operation or logging out and logging back in, make sure to mention it in the alert to help fix the user experience in real-time.
In the case of errors that don't cause any interference with the everyday user experience, you can consider suppressing the alert and logging the error to a remote server for resolving later.
5. Implement a Middleware (Node.js)
The Node.js environment supports middlewares to add functionalities to server applications. You can use this feature to create an error-handling middleware for your server.
The most significant benefit of using middleware is that all of your errors are handled centrally in one place. You can choose to enable/disable this setup for testing purposes easily.
Here's how you can create a basic middleware:
const logError = err => { console.log("ERROR: " + String(err)) } const errorLoggerMiddleware = (err, req, res, next) => { logError(err) next(err) } const returnErrorMiddleware = (err, req, res, next) => { res.status(err.statusCode || 500) .send(err.message) } module.exports = { logError, errorLoggerMiddleware, returnErrorMiddleware }
Puoi quindi utilizzare questo middleware nella tua app in questo modo:
const { errorLoggerMiddleware, returnErrorMiddleware } = require('./errorMiddleware') app.use(errorLoggerMiddleware) app.use(returnErrorMiddleware)
È ora possibile definire una logica personalizzata all'interno del middleware per gestire gli errori in modo appropriato. Non devi più preoccuparti di implementare costrutti di gestione degli errori individuali in tutta la tua base di codice.
6. Riavvia la tua app per gestire gli errori del programmatore (Node.js)
Quando le app Node.js riscontrano errori del programmatore, potrebbero non generare necessariamente un'eccezione e provare a chiudere l'app. Tali errori possono includere problemi derivanti da errori del programmatore, come consumo elevato della CPU, rigonfiamento della memoria o perdite di memoria. Il modo migliore per gestirli è riavviare con grazia l'app arrestandola in modo anomalo tramite la modalità cluster Node.js o uno strumento unico come PM2. Ciò può garantire che l'app non si arresti in modo anomalo in seguito all'azione dell'utente, presentando un'esperienza utente terribile.
7. Cattura tutte le eccezioni non rilevate (Node.js)
Non puoi mai essere sicuro di aver coperto tutti i possibili errori che possono verificarsi nella tua app. Pertanto, è essenziale implementare una strategia di fallback per rilevare tutte le eccezioni non rilevate dalla tua app.
Ecco come puoi farlo:
process.on('uncaughtException', error => { console.log("ERROR: " + String(error)) // other handling mechanisms })
È inoltre possibile identificare se l'errore che si è verificato è un'eccezione standard o un errore operativo personalizzato. In base al risultato, è possibile uscire dal processo e riavviarlo per evitare comportamenti imprevisti.
8. Cattura tutti i rifiuti di promesse non gestiti (Node.js)
Simile a come non puoi mai coprire tutte le possibili eccezioni, c'è un'alta probabilità che potresti perdere la gestione di tutti i possibili rifiuti di promesse. Tuttavia, a differenza delle eccezioni, il rifiuto delle promesse non genera errori.
Quindi, un'importante promessa che è stata rifiutata potrebbe sfuggire come avvertimento e lasciare la tua app aperta alla possibilità di incappare in comportamenti imprevisti. Pertanto, è fondamentale implementare un meccanismo di fallback per gestire il rifiuto delle promesse.
Ecco come puoi farlo:
const promiseRejectionCallback = error => { console.log("PROMISE REJECTED: " + String(error)) } process.on('unhandledRejection', callback)
twittare Sommario
Come qualsiasi altro linguaggio di programmazione, gli errori sono abbastanza frequenti e naturali in JavaScript. In alcuni casi, potresti persino dover generare errori intenzionalmente per indicare la risposta corretta ai tuoi utenti. Quindi, capire la loro anatomia e tipi è molto cruciale.
Inoltre, è necessario disporre degli strumenti e delle tecniche corretti per identificare ed evitare che gli errori annullino l'applicazione.
Nella maggior parte dei casi, una solida strategia per gestire gli errori con un'esecuzione attenta è sufficiente per tutti i tipi di applicazioni JavaScript.
Ci sono altri errori JavaScript che non sei ancora riuscito a risolvere? Qualche tecnica per gestire gli errori JS in modo costruttivo? Fateci sapere nei commenti qui sotto!