Welcome to Our Website

Errori personalizzati, estensione dell’errore

Quando sviluppiamo qualcosa, spesso abbiamo bisogno delle nostre classi di errore per riflettere cose specifiche che potrebbero andare male nei nostri compiti. Per errori nelle operazioni di rete potremmo aver bisogno di HttpError, per operazioni di database DbError, per operazioni di ricerca NotFoundError e così via.

I nostri errori dovrebbero supportare le proprietà di errore di base comemessage,namee, preferibilmente,stack. Ma possono anche avere altre proprietà proprie, ad esempio, HttpError gli oggetti possono avere una proprietàstatusCode con un valore come404 o403 o500.

JavaScript consente di utilizzare throwcon qualsiasi argomento, quindi tecnicamente le nostre classi di errore personalizzate non devono ereditare daError. Ma se ereditiamo, diventa possibile utilizzareobj instanceof Error per identificare gli oggetti di errore. Quindi è meglio ereditare da esso.

Man mano che l’applicazione cresce, i nostri errori formano naturalmente una gerarchia., Ad esempio, HttpTimeoutError può ereditare da HttpError e così via.

Errore di estensione

Ad esempio, consideriamo una funzione readUser(json) che dovrebbe leggere JSON con i dati dell’utente.

Ecco un esempio di come potrebbe apparire un json valido:

Internamente, useremo JSON.parse. Se riceve malformato json, allora lancia SyntaxError., Ma anche se json è sintatticamente corretto, ciò non significa che sia un utente valido, giusto? Potrebbe mancare i dati necessari. Ad esempio, potrebbe non avere proprietà name e age essenziali per i nostri utenti.

La nostra funzione readUser(json) non solo legge JSON, ma controlla (“convalida”) i dati. Se non ci sono campi obbligatori o il formato è errato, allora si tratta di un errore. E questo non è un SyntaxError, perché i dati sono sintatticamente corretti, ma un altro tipo di errore., Lo chiameremo ValidationError e creeremo una classe per questo. Un errore di questo tipo dovrebbe anche portare le informazioni sul campo incriminato.

La nostra classe ValidationError dovrebbe ereditare dalla classe Error incorporata.

Quella classe è integrata, ma ecco il suo codice approssimativo in modo da poter capire cosa stiamo estendendo:

Ora ereditiamoValidationError da esso e proviamolo in azione:

Nota: nella riga(1) chiamiamo il costruttore genitore., JavaScript ci richiede di chiamare super nel costruttore figlio, quindi è obbligatorio. Il costruttore padre imposta la proprietàmessage.

Il costruttore genitore imposta anche la proprietà name su "Error", quindi nella riga (2) lo ripristiniamo al valore giusto.,

Proviamo ad usarlo inreadUser(json):

Il bloccotry..catch nel codice sopra gestisce sia il nostroValidationError che il built-inSyntaxError daJSON.parse.

Si prega di dare un’occhiata a come usiamoinstanceof per verificare il tipo di errore specifico nella riga(*).,

Si potrebbe anche guardare err.name in questo modo:

// ...// instead of (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...

instanceof versione è molto meglio, perché, in futuro, abbiamo intenzione di estendere ValidationError fare sottotipi, come il PropertyRequiredError. Einstanceof il controllo continuerà a funzionare per le nuove classi ereditarie. Quindi è a prova di futuro.

Inoltre è importante che secatch incontra un errore sconosciuto, allora lo ripassa nella riga(**)., Il bloccocatch sa solo come gestire errori di convalida e sintassi, altri tipi (a causa di un errore di battitura nel codice o altri sconosciuti) dovrebbero cadere.

Ulteriore ereditarietà

La classeValidationError è molto generica. Molte cose possono andare male. La proprietà potrebbe essere assente o potrebbe essere in un formato errato (come un valore stringa per age). Facciamo una classe più concreta PropertyRequiredError, esattamente per le proprietà assenti. Porterà ulteriori informazioni sulla proprietà che manca.,

La nuova classe PropertyRequiredError è facile da usare: abbiamo solo bisogno di passare il nome della proprietà: new PropertyRequiredError(property). Il message leggibile dall’uomo viene generato dal costruttore.

Si prega di notare che this.name in PropertyRequiredError costruttore viene nuovamente assegnato manualmente. Potrebbe diventare un po ‘ noioso – assegnare this.name = <class name> in ogni classe di errore personalizzata. Possiamo evitarlo creando la nostra classe” basic error “che assegna this.name = this.constructor.name. E poi ereditare tutti i nostri errori personalizzati da esso.,

Chiamiamolo MyError.

Ecco il codice conMyError e altre classi di errore personalizzate, semplificate:

Ora gli errori personalizzati sono molto più brevi, in particolareValidationError, mentre ci siamo sbarazzati della riga"this.name = ..." nel costruttore.

Wrapping exceptions

Lo scopo della funzionereadUser nel codice sopra è “leggere i dati dell’utente”. Ci possono verificarsi diversi tipi di errori nel processo., In questo momento abbiamo SyntaxErroreValidationError, ma in futuroreadUser la funzione potrebbe crescere e probabilmente generare altri tipi di errori.

Il codice che chiama readUser dovrebbe gestire questi errori. In questo momento utilizza piùifnel bloccocatch, che controllano la classe e gestiscono errori noti e riprendono quelli sconosciuti.

Lo schema è così:

Nel codice sopra possiamo vedere due tipi di errori, ma ce ne possono essere di più.,

Se la funzione readUser genera diversi tipi di errori, allora dovremmo chiederci: vogliamo davvero controllare tutti i tipi di errore uno per uno ogni volta?

Spesso la risposta è “No”: vorremmo essere”un livello sopra tutto”. Vogliamo solo sapere se c’è stato un “errore di lettura dei dati” – perché esattamente è successo è spesso irrilevante (il messaggio di errore lo descrive). O, ancora meglio, ci piacerebbe avere un modo per ottenere i dettagli dell’errore, ma solo se ne abbiamo bisogno.

La tecnica che descriviamo qui si chiama “wrapping exceptions”.,

  1. Creeremo una nuova classeReadError per rappresentare un errore generico di “lettura dei dati”.
  2. La funzionereadUser catturerà gli errori di lettura dei dati che si verificano al suo interno, comeValidationError eSyntaxError, e genererà invece unReadError.
  3. L’oggettoReadErrormanterrà il riferimento all’errore originale nella sua proprietàcause.,

Quindi il codice che chiama readUserdovrà solo controllare ReadError, non per ogni tipo di errore di lettura dei dati. E se ha bisogno di maggiori dettagli su un errore, può controllare la sua proprietà cause.,

Ecco il codice che definisce ReadError e dimostra il suo uso nel readUser e try..catch:

Nel codice di cui sopra, readUser funziona esattamente come descritto catture sintassi e gli errori di convalida e getta ReadError errori invece (sconosciuto errori sono rigenerata, come al solito).

Quindi il codice esterno controllainstanceof ReadError e il gioco è fatto. Non c’è bisogno di elencare tutti i possibili tipi di errore.,

L’approccio è chiamato “wrapping exceptions”, perché prendiamo eccezioni di “basso livello” e le “avvolgiamo” inReadError che è più astratto. È ampiamente usato nella programmazione orientata agli oggetti.

Sommario

  • Possiamo ereditare daError e altre classi di errore built-in normalmente. Dobbiamo solo occuparci della proprietànamee non dimenticare di chiamare super.
  • Possiamo usare instanceof per verificare la presenza di errori particolari. Funziona anche con l’ereditarietà., Ma a volte abbiamo un oggetto error proveniente da una libreria di terze parti e non c’è un modo semplice per ottenere la sua classe. Quindi la proprietàname può essere utilizzata per tali controlli.
  • Il wrapping delle eccezioni è una tecnica diffusa: una funzione gestisce eccezioni di basso livello e crea errori di livello superiore invece di vari di basso livello. Le eccezioni di basso livello a volte diventano proprietà di quell’oggetto come err.cause negli esempi precedenti, ma non è strettamente richiesto.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *