Błędy
Porozmawiajmy o błędach.
Istnieją dwa rodzaje błędów:
- oczekiwane błędy
- nieoczekiwane błędy
- błędy off-by-one
Oczekiwane błędy
Oczekiwane błędy to stany, w których dzieje się coś złego, ale wiesz, że tak, więc sobie z tym poradzisz.
Są to rzeczy takie jak dane wejściowe użytkownika lub żądania serwera. Wiesz, że użytkownik może popełnić błąd lub serwer może nie działać, więc napisz kod sprawdzający, aby upewnić się, że program ponownie poprosi o wprowadzenie danych, wyświetli komunikat lub cokolwiek innego, co jest odpowiednie.
Można je odzyskać, gdy są obsługiwane. Pozostawione bez obsługi stają się nieoczekiwanymi błędami.
Nieoczekiwane błędy
Nieoczekiwane błędy (błędy) to stany, w których dzieje się coś złego, ponieważ kod jest nieprawidłowy. Wiesz, że w końcu się zdarzą, ale nie ma sposobu, aby wiedzieć, gdzie i jak sobie z nimi poradzić, ponieważ z definicji są nieoczekiwane.
Są to między innymi błędy składniowe i logiczne. Być może masz literówkę w kodzie, mogłeś wywołać funkcję z niewłaściwymi parametrami. Zazwyczaj nie można ich odzyskać.
try..catch
Porozmawiajmy o tym try..catch
.
W JavaScript throw
nie jest powszechnie używany. Jeśli rozejrzysz się za przykładami w kodzie, to będzie ich niewiele i zwykle są zbudowane według linii
function example(param) {
if (!Array.isArray(param) {
throw new TypeError('"param" should be an array!');
}
...
}
Z tego powodu try..catch
bloki nie są tak często spotykane w przepływie sterowania. Zazwyczaj dość łatwo jest dodać kilka sprawdzeń przed wywołaniem metod, aby uniknąć oczekiwanych błędów.
Środowiska JavaScript są również dość wybaczające, więc nieoczekiwane błędy również często pozostają niezauważone.
try..catch
nie musi być rzadkie. Istnieje kilka fajnych przypadków użycia, które są bardziej powszechne w językach takich jak Java i C #. Java i C # mają tę zaletę catch
, że można je rozróżnić między oczekiwanymi i nieoczekiwanymi błędami:
C # :
try
{
var example = DoSomething();
}
catch (ExpectedException e)
{
DoSomethingElse(e);
}
Ten przykład pozwala przepłynąć innym nieoczekiwanym wyjątkom i zająć się nimi gdzie indziej (na przykład poprzez zalogowanie i zamknięcie programu).
W JavaScript ta konstrukcja może być replikowana poprzez:
try {
let example = doSomething();
} catch (e) {
if (e instanceOf ExpectedError) {
DoSomethingElse(e);
} else {
throw e;
}
}
Nie tak elegancki, co jest jednym z powodów, dla których jest to rzadkie.
Funkcje
Porozmawiajmy o funkcjach.
Jeśli zastosujesz zasadę pojedynczej odpowiedzialności , każda klasa i funkcja powinna służyć jednemu celowi.
Na przykład authenticate()
może uwierzytelnić użytkownika.
Może to być zapisane jako:
const user = authenticate();
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
Alternatywnie można go zapisać jako:
try {
const user = authenticate();
// keep doing stuff
} catch (e) {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
}
Oba są dopuszczalne.
Obietnice
Porozmawiajmy o obietnicach.
Obietnice są formą asynchroniczną try..catch
. Dzwonię new Promise
lub Promise.resolve
uruchamia try
kod. Dzwonię throw
lub Promise.reject
wysyła cię na catch
kod.
Promise.resolve(value) // try
.then(doSomething) // try
.then(doSomethingElse) // try
.catch(handleError) // catch
Jeśli masz funkcję asynchroniczną do uwierzytelnienia użytkownika, możesz zapisać go jako:
authenticate()
.then((user) => {
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
});
Alternatywnie można go zapisać jako:
authenticate()
.then((user) => {
// keep doing stuff
})
.catch((e) => {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
});
Oba są dopuszczalne.
Zagnieżdżanie
Porozmawiajmy o zagnieżdżaniu.
try..catch
można zagnieżdżać. Twoja authenticate()
metoda może mieć try..catch
blok wewnętrzny, taki jak:
try {
const credentials = requestCredentialsFromUser();
const user = getUserFromServer(credentials);
} catch (e) {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
}
Podobnie obietnice mogą być zagnieżdżone. Twoja authenticate()
metoda asynchroniczna może wewnętrznie wykorzystywać obietnice:
requestCredentialsFromUser()
.then(getUserFromServer)
.catch((e) => {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
});
Więc jaka jest odpowiedź?
Ok, myślę, że nadszedł czas, abym faktycznie odpowiedział na pytanie:
Czy błąd uwierzytelnienia jest czymś, co można odrzucić obietnicą?
Najprostszą odpowiedzią, jaką mogę udzielić, jest to, że powinieneś odrzucić obietnicę w dowolnym miejscu, w którym inaczej byłby throw
wyjątek, gdyby był to kod synchroniczny.
Jeśli przepływ kontroli jest prostszy dzięki kilku if
kontrolom w then
wyciągach, nie musisz odrzucać obietnicy.
Jeśli przepływ kontroli jest prostszy, odrzucając obietnicę, a następnie sprawdzając rodzaje błędów w kodzie obsługi błędów, zrób to zamiast tego.
reject
i nie zwróci false, ale jeśli spodziewasz wartość byćBool
, to były udane i należy rozwiązać z Bool niezależnie od wartości. Obietnice są swego rodzaju proxy dla wartości - przechowują zwróconą wartość, więc tylko wtedy, gdy nie można jej uzyskaćreject
. W przeciwnym razie powinieneśresolve
.