Non importa quanto siamo bravi a programmare, a volte i nostri script hanno errori. Possono verificarsi a causa dei nostri errori, un input inaspettato dell’utente, una risposta errata del server e per mille altri motivi.
Di solito, uno script “muore” (si ferma immediatamente) in caso di errore, stampandolo sulla console.
Ma c’è un costrutto di sintassi try..catch
che ci permette di “catturare” gli errori in modo che lo script possa, invece di morire, fare qualcosa di più ragionevole.,
Il “try…catch” sintassi
try..catch
costruire dispone di due blocchi principali: try
e poi catch
:
try { // code...} catch (err) { // error handling}
funziona in questo modo:
- in Primo luogo, il codice
try {...}
viene eseguito. - Se non ci sono stati errori, allora
catch(err)
viene ignorato: l’esecuzione raggiunge la fine ditry
e va avanti, saltandocatch
., - Se si verifica un errore, l’esecuzione di
try
viene interrotta e il controllo scorre all’inizio dicatch(err)
. La variabileerr
(possiamo usare qualsiasi nome per essa) conterrà un oggetto error con dettagli su ciò che è successo.
Così, un errore all’interno del try {…}
blocco non uccidere gli script – abbiamo la possibilità di gestire in catch
.,
Diamo un’occhiata ad alcuni esempi.
try..catch
funziona solo per errori di runtime Per try..catch
per funzionare, il codice deve essere eseguibile. In altre parole, dovrebbe essere JavaScript valido.
Non funzionerà se il codice è sintatticamente sbagliato, ad esempio ha parentesi graffe senza pari:
try { {{{{{{{{{{{{} catch(e) { alert("The engine can't understand this code, it's invalid");}
Il motore JavaScript prima legge il codice e poi lo esegue., Gli errori che si verificano nella fase di lettura sono chiamati errori” parse-time ” e sono irrecuperabili (dall’interno di quel codice). Questo perché il motore non riesce a capire il codice.
Quindi,try..catch
può gestire solo gli errori che si verificano nel codice valido. Tali errori sono chiamati ” errori di runtime “o, a volte,”eccezioni”.
Error object
Quando si verifica un errore, JavaScript genera un oggetto contenente i dettagli su di esso., L’oggetto viene quindi passato come argomento a catch
:
Per tutti gli errori incorporati, l’oggetto error ha due proprietà principali:
name
Nome errore. Ad esempio, per una variabile indefinita che è "ReferenceError"
. message
Messaggio testuale sui dettagli dell’errore.
Ci sono altre proprietà non standard disponibili nella maggior parte degli ambienti., Uno dei più utilizzati e supportati è:
stack
Current call stack: una stringa con informazioni sulla sequenza di chiamate nidificate che hanno portato all’errore. Utilizzato per scopi di debug.
Per esempio:
Binding opzionale “catch”
Se non abbiamo bisogno di dettagli errore, catch
può omettere:
try { // ...} catch { // <-- without (err) // ...}
Uso di “try…catch”
esploriamo la vita reale caso di utilizzo di try..catch
.
Come già sappiamo, JavaScript supporta il JSON.metodo parse (str) per leggere i valori codificati JSON.
Di solito viene utilizzato per decodificare i dati ricevuti in rete, dal server o da un’altra fonte.,
Lo riceviamo e chiamiamoJSON.parse
in questo modo:
Puoi trovare informazioni più dettagliate su JSON nei metodi JSON, capitolo toJSON.
Se json
è malformato, JSON.parse
genera un errore, quindi lo script “muore”.
Dovremmo essere soddisfatti di questo? Certo che no!
In questo modo, se qualcosa non va con i dati, il visitatore non lo saprà mai (a meno che non apra la console degli sviluppatori). E alla gente non piace davvero quando qualcosa “muore” senza alcun messaggio di errore.,
Usiamotry..catch
per gestire l’errore:
Qui usiamo il bloccocatch
solo per mostrare il messaggio, ma possiamo fare molto di più: inviare una nuova richiesta di rete, suggerire un’alternativa al visitatore, inviare informazioni sull’errore a un impianto di registrazione, … . Tutto molto meglio che morire.
Lanciare i nostri errori
Cosa succede sejson
è sintatticamente corretto, ma non ha una proprietàname
richiesta?,
let json = '{ "age": 30 }'; // incomplete datatry { let user = JSON.parse(json); // <-- no errors alert( user.name ); // no name!} catch (e) { alert( "doesn't execute" );}
JSON.parse
viene eseguito normalmente, ma l’assenza di name
è in realtà un errore per noi.
Per unificare la gestione degli errori, useremo l’operatore throw
.
Operatore”Throw”
L’operatorethrow
genera un errore.
La sintassi è:
throw <error object>
Tecnicamente, possiamo usare qualsiasi cosa come oggetto di errore., Potrebbe essere anche una primitiva, come un numero o una stringa, ma è meglio usare oggetti, preferibilmente con proprietà name
e message
(per rimanere in qualche modo compatibili con gli errori incorporati).
JavaScript ha molti costruttori incorporati per errori standard: Error
, SyntaxError
, ReferenceError
, TypeError
e altri. Possiamo usarli anche per creare oggetti di errore.,
La loro sintassi è:
Per errori incorporati (non per oggetti, solo per errori), la proprietà name
è esattamente il nome del costruttore. Emessage
è preso dall’argomento.,
Per esempio:
let error = new Error("Things happen o_O");alert(error.name); // Erroralert(error.message); // Things happen o_O
vediamo che tipo di errore JSON.parse
genera:
try { JSON.parse("{ bad json o_O }");} catch(e) { alert(e.name); // SyntaxError alert(e.message); // Unexpected token b in JSON at position 2}
Come si può vedere, che il SyntaxError
.
E nel nostro caso, l’assenza di name
è l’errore, poiché gli utenti devono avere un name
.,
Quindi buttiamolo:
Nella riga(*)
, l’operatorethrow
genera unSyntaxError
con il datomessage
, allo stesso modo di JavaScript genererebbe esso stesso. L’esecuzione di try
si interrompe immediatamente e il flusso di controllo salta in catch
.
Ora catch
è diventato un unico posto per tutta la gestione degli errori: sia per JSON.parse
e altri casi.,
Rethrowing
Nell’esempio sopra usiamotry..catch
per gestire dati errati. Ma è possibile che si verifichi un altro errore imprevisto all’interno del blocco try {...}
? Come un errore di programmazione (la variabile non è definita) o qualcos’altro, non solo questa cosa “dati errati”.
Per esempio:
Certo, tutto è possibile! I programmatori fanno errori. Anche nelle utility open-source utilizzate da milioni di persone per decenni – improvvisamente si può scoprire un bug che porta a terribili hack.,
Nel nostro caso, try..catch
viene inserito per rilevare errori di “dati errati”. Ma per sua natura,catch
ottiene tutti gli errori datry
. Qui ottiene un errore inaspettato, ma mostra ancora lo stesso messaggio"JSON Error"
. Questo è sbagliato e rende anche il codice più difficile da eseguire il debug.
Per evitare tali problemi, possiamo utilizzare la tecnica “rethrowing”. La regola è semplice:
Catch dovrebbe elaborare solo errori che conosce e “ripensare” tutti gli altri.,
La tecnica “rethrowing” può essere spiegata in modo più dettagliato come:
- Catch ottiene tutti gli errori.
- Nel blocco
catch(err) {...}
analizziamo l’oggetto di erroreerr
. - Se non sappiamo come gestirlo, facciamo
throw err
.
Di solito, possiamo controllare il tipo di errore usando l’operatore instanceof
:
Possiamo anche ottenere il nome della classe di errore dalla proprietà err.name
. Tutti gli errori nativi ce l’hanno. Un’altra opzione è leggere err.constructor.name
.,
Nel codice riportato di seguito, usiamo generazione ripetuta in modo che catch
gestisce solo SyntaxError
:
L’errore di lancio on line (*)
dall’interno catch
blocco “cade” di try..catch
e può essere catturato da un esterno try..catch
costruire (se esiste), o che uccide lo script.
Quindi il bloccocatch
gestisce in realtà solo errori che sa come gestire e “salta” tutti gli altri.,
L’esempio seguente mostra come tali errori possono essere rilevati da un altro livello di try..catch
:
Qui readData
sa solo come gestire SyntaxError
, mentre l’esterno try..catch
sa come gestire tutto.
prova catch cattura finally finalmente
Aspetta, non è tutto.
Il costrutto try..catch
può avere un’altra clausola di codice: finally
.,
Se esiste, viene eseguito in tutti i casi:
- dopo
try
, se non ci sono stati errori, - dopo
catch
, se ci sono stati errori.
La sintassi estesa di simile a questo:
try { ... try to execute the code ...} catch(e) { ... handle errors ...} finally { ... execute always ...}
Provare a eseguire questo codice:
try { alert( 'try' ); if (confirm('Make an error?')) BAD_CODE();} catch (e) { alert( 'catch' );} finally { alert( 'finally' );}
Il codice ha due modalità di esecuzione:
- Se la risposta è “Sì” per “Fare un errore?”, quindi
try -> catch -> finally
., - Se dici “No”, allora
try -> finally
.
La clausola finally
viene spesso utilizzata quando iniziamo a fare qualcosa e vogliamo finalizzarla in ogni caso di esito.
Ad esempio, vogliamo misurare il tempo che una funzione di numeri di Fibonaccifib(n)
richiede. Naturalmente, possiamo iniziare a misurare prima che venga eseguito e finire in seguito. Ma cosa succede se c’è un errore durante la chiamata alla funzione? In particolare, l’implementazione di fib(n)
nel codice seguente restituisce un errore per numeri negativi o non interi.,
La clausolafinally
è un ottimo posto per completare le misurazioni, indipendentemente da cosa.
finally
garantisce che il tempo sarà misurato correttamente in entrambe le situazioni – in caso di esito positivo dell’esecuzione del fib
e in caso di errore:
È possibile controllare l’esecuzione del codice, con l’immissione di 35
in prompt
esegue normalmente finally
dopo try
., E poi inserisci -1
– ci sarà un errore immediato e l’esecuzione prenderà0ms
. Entrambe le misurazioni sono eseguite correttamente.
In altre parole, la funzione potrebbe finire conreturn
othrow
, non importa. La clausolafinally
viene eseguita in entrambi i casi.,
try..catch..finally
Si prega di notare che result
e diff
le variabili nel codice sopra sono dichiarate prima di try..catch
.
Altrimenti, se dichiarassimolet
intry
blocco, sarebbe visibile solo all’interno di esso.
Iltry..finally
costrutto, senza catch
clausola, è anche utile., Lo applichiamo quando non vogliamo gestire gli errori qui (lasciarli cadere), ma vogliamo essere sicuri che i processi che abbiamo iniziato siano finalizzati.
Nel codice precedente, un errore all’interno ditry
cade sempre, perché non c’è catch
. Mafinally
funziona prima che il flusso di esecuzione lasci la funzione.
Cattura globale
Le informazioni di questa sezione non fanno parte del JavaScript principale.,
Immaginiamo di avere un errore fatale al di fuori di try..catch
, e lo script è morto. Come un errore di programmazione o qualche altra cosa terribile.
C’è un modo per reagire a tali eventi? Potremmo voler registrare l’errore, mostrare qualcosa all’utente (normalmente non vedono i messaggi di errore), ecc.
Non ce n’è nella specifica, ma gli ambienti di solito lo forniscono, perché è davvero utile. Ad esempio, Nodo.js haprocess.on("uncaughtException")
per quello. E nel browser possiamo assegnare una funzione alla finestra speciale.,proprietà onerror, che verrà eseguito in caso di errore non rilevato.
La sintassi:
window.onerror = function(message, url, line, col, error) { // ...};
message
Messaggio di errore. url
URL dello script in cui si è verificato l’errore. line
,col
Numeri di riga e colonna in cui si è verificato un errore. error
Oggetto di errore.,
Ad esempio:
Il ruolo del gestore globale window.onerror
di solito non è quello di recuperare l’esecuzione dello script – probabilmente impossibile in caso di errori di programmazione, ma di inviare il messaggio di errore agli sviluppatori.
Funzionano così:
- Ci registriamo al servizio e otteniamo un pezzo di JS (o un URL di script) da inserire nelle pagine.
- Che lo script JS imposta una funzione
window.onerror
personalizzata. - Quando si verifica un errore, invia una richiesta di rete al servizio.
- Possiamo accedere all’interfaccia web del servizio e vedere gli errori.,
Riepilogo
Il costruttotry..catch
consente di gestire gli errori di runtime. Permette letteralmente di” provare “a eseguire il codice e” catturare” gli errori che possono verificarsi in esso.
La sintassi è:
Non ci possono essere sezioni catch
o no finally
, quindi sono validi anche costrutti più brevi try..catch
e try..finally
.
Gli oggetti Error hanno le seguenti proprietà:
message
– il messaggio di errore leggibile dall’uomo.,-
name
– la stringa con il nome dell’errore (error constructor name). -
stack
(non standard, ma ben supportato)-lo stack al momento della creazione dell’errore.
Se non è necessario un oggetto error, possiamo ometterlo usando catch {
invece di catch(err) {
.
Possiamo anche generare i nostri errori utilizzando l’operatore throw
., Tecnicamente, l’argomento di throw
può essere qualsiasi cosa, ma di solito è un oggetto error che eredita dalla classe Error
incorporata. Maggiori informazioni sull’estensione degli errori nel prossimo capitolo.
Il Rethrowing è un modello molto importante di gestione degli errori: un blocco catch
di solito si aspetta e sa come gestire il particolare tipo di errore, quindi dovrebbe ripetere gli errori che non conosce.
Anche se non abbiamo try..catch
, la maggior parte degli ambienti ci consente di impostare un gestore di errori “globale” per rilevare errori che “cadono”., In-browser, questo è window.onerror
.