Personalizzazione degli errori nel nodo.js

Javascript integrato Error fornisce informazioni utili, ma spesso può sembrare privo di chiarezza. One-size-fits-all è grande per la lingua, ma possiamo fare meglio nelle nostre applicazioni. Ecco dove arrivano gli errori personalizzati.

Potresti aver visto errori personalizzati durante l’utilizzo del nodo.js. Tipi di errore incorporati del nodo come AssertionError, RangeError, ReferenceError, SyntaxError, e SystemError sono tutte estensioni della classe nativa Error.

L’utilizzo di Error come base offre tutta la potenza dell’implementazione di Javascript e ulteriori vantaggi. Qui ci sono solo alcuni:

  • Formati di messaggi di errore personalizzati.
  • Proprietà aggiuntive sull’oggetto Error.
  • Errori denominati per semplificare il debug e la gestione degli errori condizionali.
  • Errori coerenti per i consumatori di librerie a cui fare riferimento.

Estensione della classe di errore

Per iniziare, usiamo un esempio generico. Estenderemo la classe Error e useremo super per ereditare le sue funzioni.

class CustomError extends Error { constructor(...params) { super(...params) // We're spreading `params` as a way to bring all of `Error`'s functionality in. }}

Ora, quando si throwun errore, è possibile farlo con throw new CustomError("Something went wrong..."). Ti dà anche la possibilità di controllare gli errori rispetto al tipo:

try { throw new CustomError("Something went wrong")} catch (error) { if (error instance of CustomError) { // do something specifically for that type of error }}

Questo da solo non fa molto, a parte darti un nuovo tipo di errore da chiamare e controllare. Per capire meglio cosa si può fare, diamo un’occhiata a ciò che viene standard su Error.

  • Error.name
  • Errore.messaggio
  • Errore.prototipo.toString ()
  • Errore.prototipo.costruttore()

Non c’è molto da lavorare? A parte il costruttore e il metodo toString, il nome dell’errore e il messaggio che descrive l’errore sono le uniche parti che probabilmente utilizzeremo. Nell’esempio sopra, message viene impostato passando “qualcosa è andato storto” come argomento quando si crea un’istanza dell’errore.

Fortunatamente, la maggior parte delle piattaforme javascript come il browser e il Nodo.js hanno aggiunto i propri metodi e proprietà in cima a quelli elencati. Ci concentreremo su alcuni aspetti dell’implementazione degli errori da V8, il motore Javascript che alimenta Chrome e Node.js.

Le due aree di interesse sono stacke captureStackTrace. Come suggeriscono i loro nomi, ti permettono di far emergere la traccia dello stack. Vediamo come appare con un esempio.

class CustomError extends Error { constructor(...args) { super(...args) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.name = "Our Custom Error" }}try { throw new CustomError("Something went wrong")} catch (err) { console.error(err.stack)}

In questo esempio stiamo chiamando Error.captureStackTrace, se la piattaforma lo supporta, per garantire che una traccia completa venga aggiunta al nostro errore personalizzato. Abbiamo quindi throw l’errore dall’interno del blocco try, che passerà il controllo al blocco catch.

Se esegui il codice sopra, vedrai che la prima riga di stack è l’errore namee message.

Our Custom Error: Something went wrong

Questo errore non è molto “profondo”, quindi lo stack è principalmente Nodo.js interni. Ogni riga è un “frame” della pila. Contengono dettagli sulla posizione dell’errore all’interno della base di codice.

Ora che sappiamo come fare e chiamare un errore personalizzato, rendiamolo utile.

Personalizzazione del contenuto dell’errore

L’errore personalizzato con cui abbiamo lavorato va bene, ma cambiiamolo per renderlo più utile. La classe sarà CircuitError. Lo useremo per fornire errori personalizzati provenienti da un interruttore. Se non hai familiarità con il modello, va bene. I dettagli di implementazione dell’interruttore non sono importanti. Ciò che è importante è che possiamo passare alcune informazioni nell’errore e presenterà tali informazioni all’utente.

Un interruttore ha uno stato “APERTO” che non consente a nulla di attraversarlo per un determinato periodo di tempo. Impostiamo il nostro CircuitError per contenere alcuni dettagli che potrebbero essere utili alle funzioni che lo ricevono quando lo stato è OPEN.

class CircuitError extends Error { // 1 constructor(state, nextAttempt, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CircuitError) } this.name = "CircuitError" this.state = state // 2 this.message = `The Circuit is ${state}.` // 3 if (nextAttempt) { this.timestamp = Date.now() this.nextAttempt = nextAttempt this.message += ` Next attempt can be made in ${this.nextAttempt - this.timestamp}ms.` } }}

Il nostro errore aggiornato fa alcune cose nuove, tutte all’interno del costruttore. Gli argomenti passati sono cambiati (1) per includere il state dell’interruttore automatico, così come un nextAttempt timestamp per indicare quando l’interruttore inizierà a provare di nuovo le richieste.

Imposta quindi alcune nuove proprietà e aggiorna il messaggio in base ai valori presentati. Possiamo testarlo lanciando una nuova versione di questo errore.

try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { console.error(err)}

Ora quando lanciamo l’errore, prende lo stato come primo argomento e un timestamp come secondo. Per questa demo stiamo passando nel tempo 8000 millisecondi in futuro. Si noti che stiamo anche registrando l’errore stesso, piuttosto che solo lo stack.

L’esecuzione del codice comporterà qualcosa di simile al seguente:

CircuitError : The Circuit is OPEN. Next attempt can be made in 6000ms. at Object.<anonymous> (/example.js:21:9) at Module._compile (internal/modules/cjs/loader.js:1063:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1103:10) at Module.load (internal/modules/cjs/loader.js:914:32) at Function.Module._load (internal/modules/cjs/loader.js:822:14) at Function.Module.runMain (internal/modules/cjs/loader.js:1143:12) at internal/main/run_main_module.js:16:11 { name: 'Circuit Error', state: 'OPEN', message: 'The Circuit is OPEN. Next attempt can be made in 6000ms.', timestamp: 1580242308919, nextAttempt: 1580242314919}

Il risultato è il nome dell’errore seguito dal messaggio sulla prima riga. Quindi abbiamo il resto dello stack, seguito da tutte le proprietà che abbiamo impostato in CircuitError. I nostri consumatori di questo errore potrebbero utilizzare tali dati per reagire all’errore. Ad esempio:

try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { customLogger(err) if (err.state === "OPEN") { handleOpenState(err.nextAttempt) }}

Invece di un errore generico, ora abbiamo qualcosa di più adatto alle esigenze della nostra applicazione.

Reagire agli errori

Gli errori personalizzati sono un ottimo modo per reagire agli errori. È comune supporre che tutti gli errori siano inaspettati, ma fornendo errori utili è possibile rendere la loro esistenza più facile da gestire. Facendo uso di errori personalizzati, non solo possiamo fornire agli utenti delle nostre applicazioni dati preziosi, ma presentiamo anche la possibilità di rispondere ai tipi di errore attraverso la condizione instance of.

A Bearer, utilizziamo errori personalizzati per standardizzare le risposte nel nostro codice, fornire solo le informazioni necessarie nel nostro cliente e rendere i nostri errori perseguibili.

Come si utilizzano errori personalizzati nelle applicazioni? Connettiti con noi @ BearerSH e fateci sapere.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.