wbudowany w Javascript Error
dostarcza użytecznych informacji, ale często może odczuwać brak jasności. Jeden rozmiar pasuje do wszystkich jest świetny dla języka, ale możemy zrobić lepiej w naszych własnych aplikacjach. Tutaj pojawiają się błędy niestandardowe.
być może zauważyłeś niestandardowe błędy podczas korzystania z węzła.js. Node jest wbudowany w typy błędów, takie jak AssertionError
, RangeError
, ReferenceError
, SyntaxError
, i SystemError
są rozszerzeniami rodzimej klasy Error
.
Korzystanie z Error
jako bazy daje całą moc implementacji Javascript plus dodatkowe korzyści. Oto kilka z nich:
- niestandardowe formaty komunikatów o błędach.
- dodatkowe właściwości obiektu błędu.
- nazwane błędy ułatwiające debugowanie i obsługę błędów warunkowych.
- spójne błędy dla użytkowników biblioteki do odwołania.
rozszerzenie klasy błędu
aby rozpocząć, użyjmy ogólnego przykładu. Rozszerzymy klasę Error
i użyjemy super
do dziedziczenia jej funkcji.
class CustomError extends Error { constructor(...params) { super(...params) // We're spreading `params` as a way to bring all of `Error`'s functionality in. }}
teraz, gdy throw
błąd, możesz to zrobić za pomocą throw new CustomError("Something went wrong...")
. Daje również możliwość sprawdzenia błędów w stosunku do typu:
try { throw new CustomError("Something went wrong")} catch (error) { if (error instance of CustomError) { // do something specifically for that type of error }}
to samo nie robi wiele, poza tym daje nowy typ błędu do wywołania i sprawdzenia. Aby lepiej zrozumieć, co można zrobić, spójrzmy na to, co jest standardem w Error
.
- Error.name
- Błąd.komunikat
- błąd.prototyp.błąd toString()
- .prototyp.konstruktor()
nie ma z czym pracować, prawda? Oprócz konstruktora i metody toString
, Nazwa błędu i komunikat opisujący błąd są jedynymi częściami, których prawdopodobnie użyjemy. W powyższym przykładzie wartość message
jest ustawiana przez podanie argumentu „coś poszło nie tak” podczas tworzenia instancji błędu.
Na szczęście większość platform javascript, takich jak przeglądarka i węzeł.js dodał swoje własne metody i właściwości na górze tych wymienionych. Skupimy się na kilku aspektach implementacji błędów z wersji V8, silnika Javascript zasilającego Chrome i Node.js.
dwa obszary zainteresowania to stack
i captureStackTrace
. Jak sugerują ich nazwy, pozwalają one wynurzyć ślad stosu. Zobaczmy, jak to wygląda na przykładzie.
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)}
W tym przykładzie wywołujemy Error.captureStackTrace
, jeśli platforma go obsługuje, aby upewnić się, że kompletny ślad zostanie dodany do naszego błędu niestandardowego. Następnie throw
błąd z bloku try
, który przekaże kontrolę do bloku catch
.
jeśli uruchomisz powyższy kod, zobaczysz, że pierwsza linia stack
jest błędem name
i message
.
Our Custom Error: Something went wrong
ten błąd nie jest zbyt „głęboki”, więc stos jest głównie węzłem.js internals. Każda linia to jedna „ramka”stosu. Zawierają one szczegóły dotyczące lokalizacji błędu w bazie kodu.
teraz, gdy wiemy, jak zrobić i wywołać błąd Niestandardowy, uczyńmy go użytecznym.
dostosowywanie zawartości błędu
błąd Niestandardowy, z którym pracowaliśmy, jest w porządku, ale zmieńmy go, aby był bardziej użyteczny. Klasa będzie CircuitError
. Użyjemy go do dostarczenia niestandardowych błędów pochodzących z wyłącznika. Jeśli nie znasz wzoru, to w porządku. Szczegóły implementacji wyłącznika nie są ważne. Ważne jest to, że możemy przekazać pewne informacje do błędu i przedstawi je użytkownikowi.
wyłącznik ma stan „otwarty”, który nie pozwala nic przejść przez niego przez określony czas. Ustawmy nasz CircuitError
tak, aby zawierał pewne szczegóły, które mogą być przydatne dla funkcji, które go odbierają, gdy stan jest 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.` } }}
nasz zaktualizowany błąd robi kilka nowych rzeczy, wszystkie w konstruktorze. Przekazane argumenty zostały zmienione (1) tak, aby zawierały state
wyłącznika, jak również nextAttempt
znacznik czasu, aby wskazać, kiedy wyłącznik zacznie ponownie próbować żądań.
następnie ustawia kilka nowych właściwości i aktualizuje wiadomość w zależności od prezentowanych wartości. Możemy go przetestować, rzucając nową wersję tego błędu.
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { console.error(err)}
teraz, gdy rzucamy błąd, przyjmuje on stan jako pierwszy argument, a znacznik czasu jako drugi. Dla tego demo mijamy w czasie 8000 milisekund w przyszłości. Zauważ, że rejestrujemy również sam błąd, a nie tylko stos.
uruchomienie kodu spowoduje coś takiego:
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}
rezultatem jest nazwa błędu, po której następuje komunikat w pierwszej linii. Następnie mamy resztę stosu, a następnie wszystkie właściwości, które ustawiliśmy w CircuitError
. Nasi konsumenci tego błędu mogą użyć tych danych, aby zareagować na błąd. Na przykład:
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { customLogger(err) if (err.state === "OPEN") { handleOpenState(err.nextAttempt) }}
zamiast ogólnego błędu, mamy teraz coś lepiej dopasowanego do potrzeb naszej aplikacji.
reagowanie na awarie
błędy niestandardowe to świetny sposób na reagowanie na awarie. Powszechnie przyjmuje się, że wszystkie błędy są nieoczekiwane, ale dostarczając użytecznych błędów, możesz ułatwić sobie ich istnienie. Korzystając z niestandardowych błędów, nie tylko możemy dostarczyć użytkownikom naszych aplikacji cenne dane, ale także prezentujemy możliwość reagowania na typy błędów za pomocą warunku instance of
.
W Bearer używamy błędów niestandardowych, aby ustandaryzować odpowiedzi w naszym kodzie, dostarczyć tylko informacje, których potrzebujesz u naszego klienta, i sprawić, że nasze błędy będą skuteczne.
jak wykorzystujesz niestandardowe błędy w swoich aplikacjach? Skontaktuj się z nami @BearerSH i daj nam znać.