Ostateczny przewodnik po obsłudze błędów w JavaScript
Opublikowany: 2022-01-24Prawo Murphy'ego mówi, że cokolwiek może się nie udać, w końcu pójdzie nie tak. Odnosi się to odrobinę za dobrze w świecie programowania. Jeśli tworzysz aplikację, prawdopodobnie stworzysz błędy i inne problemy. Błędy w JavaScript to jeden z takich powszechnych problemów!
Sukces oprogramowania zależy od tego, jak dobrze jego twórcy mogą rozwiązać te problemy, zanim zaszkodzą użytkownikom. A JavaScript, ze wszystkich języków programowania, jest znany ze swojego przeciętnego projektu obsługi błędów.
Jeśli tworzysz aplikację JavaScript, istnieje duże prawdopodobieństwo, że w pewnym momencie popełnisz błąd z typami danych. Jeśli tak nie jest, możesz w końcu zastąpić undefined operatorem wartości null lub potrójnej równości ( ===
) operatorem podwójnej równości ( ==
).
Tylko człowiek popełnia błędy. Dlatego pokażemy Ci wszystko, co musisz wiedzieć o obsłudze błędów w JavaScript.
Ten artykuł poprowadzi Cię przez podstawowe błędy w JavaScript i wyjaśni różne błędy, które możesz napotkać. Dowiesz się wtedy, jak identyfikować i naprawiać te błędy. Istnieje również kilka wskazówek, jak skutecznie radzić sobie z błędami w środowiskach produkcyjnych.
Bez zbędnych ceregieli zacznijmy!
Co to są błędy JavaScript?
Błędy w programowaniu odnoszą się do sytuacji, które nie pozwalają programowi normalnie funkcjonować. Może się to zdarzyć, gdy program nie wie, jak obsłużyć dane zadanie, na przykład podczas próby otwarcia nieistniejącego pliku lub nawiązania kontaktu z punktem końcowym internetowego interfejsu API, gdy nie ma połączenia sieciowego.
Takie sytuacje zmuszają program do zgłaszania błędów użytkownikowi, stwierdzając, że nie wie, jak postępować. Program zbiera jak najwięcej informacji o błędzie, a następnie zgłasza, że nie może ruszyć dalej.
Inteligentni programiści starają się przewidzieć i uwzględnić te scenariusze, aby użytkownik nie musiał samodzielnie wymyślać komunikatu o błędzie technicznym, takiego jak „404”. Zamiast tego pokazują znacznie bardziej zrozumiały komunikat: „Nie można znaleźć strony”.
Błędy w JavaScript to obiekty wyświetlane za każdym razem, gdy wystąpi błąd programistyczny. Obiekty te zawierają obszerne informacje o typie błędu, instrukcji, która spowodowała błąd, oraz śladzie stosu w momencie wystąpienia błędu. JavaScript umożliwia również programistom tworzenie niestandardowych błędów, aby zapewnić dodatkowe informacje podczas debugowania problemów.
Właściwości błędu
Teraz, gdy definicja błędu JavaScript jest jasna, czas zagłębić się w szczegóły.
Błędy w JavaScript zawierają pewne standardowe i niestandardowe właściwości, które pomagają zrozumieć przyczynę i skutki błędu. Domyślnie błędy w JavaScript zawierają trzy właściwości:
- message : wartość ciągu, która zawiera komunikat o błędzie
- nazwa : typ błędu, który wystąpił (zagłębimy się w to szczegółowo w następnej sekcji)
- stos : ślad stosu kodu wykonanego, gdy wystąpił błąd.
Dodatkowo błędy mogą również przenosić właściwości, takie jak numer_kolumny, numer_linii, nazwa_pliku itp., aby lepiej opisać błąd. Jednak te właściwości nie są standardowe i mogą, ale nie muszą być obecne w każdym obiekcie błędu wygenerowanym z aplikacji JavaScript.
Zrozumienie śledzenia stosu
Ślad stosu to lista wywołań metod, w których znajdował się program, gdy wystąpi zdarzenie, takie jak wyjątek lub ostrzeżenie. Tak wygląda przykładowy zrzut stosu, któremu towarzyszy wyjątek:

Jak widać, zaczyna się od wydrukowania nazwy i komunikatu o błędzie, a następnie listy wywoływanych metod. Każde wywołanie metody określa lokalizację swojego kodu źródłowego i wiersz, w którym zostało wywołane. Możesz użyć tych danych, aby poruszać się po bazie kodu i identyfikować, który fragment kodu powoduje błąd.
Ta lista metod jest ułożona w stos. Pokazuje, gdzie Twój wyjątek został zgłoszony po raz pierwszy i jak został rozpropagowany przez wywołania metod skumulowanych. Zaimplementowanie przechwycenia wyjątku nie pozwoli na jego propagację przez stos i awarię programu. Jednak możesz chcieć pozostawić błędy krytyczne, aby celowo zawiesić program w niektórych scenariuszach.
Błędy a wyjątki
Większość ludzi zwykle uważa błędy i wyjątki za to samo. Należy jednak zwrócić uwagę na niewielką, ale fundamentalną różnicę między nimi.
Aby lepiej to zrozumieć, weźmy szybki przykład. Oto jak możesz zdefiniować błąd w JavaScript:
const wrongTypeError = TypeError("Wrong type found, expected character")
I tak obiekt wrongTypeError
staje się wyjątkiem:
throw wrongTypeError
Jednak większość ludzi ma tendencję do używania skróconej formy, która definiuje obiekty błędów podczas ich rzucania:
throw TypeError("Wrong type found, expected character")
To jest standardowa praktyka. Jest to jednak jeden z powodów, dla których programiści mają tendencję do mieszania wyjątków i błędów. Dlatego znajomość podstaw jest niezbędna, nawet jeśli używasz skrótów, aby szybko wykonać swoją pracę.
Rodzaje błędów w JavaScript
W JavaScript istnieje szereg predefiniowanych typów błędów. Są one automatycznie wybierane i definiowane przez środowisko wykonawcze JavaScript, gdy programista nie obsługuje jawnie błędów w aplikacji.
Ta sekcja przeprowadzi Cię przez niektóre z najczęstszych typów błędów w JavaScript i zrozumie, kiedy i dlaczego występują.
Błąd zakresu
RangeError jest generowany, gdy zmienna jest ustawiona na wartość spoza dozwolonego zakresu wartości. Zwykle występuje podczas przekazywania wartości jako argumentu do funkcji, a podana wartość nie leży w zakresie parametrów funkcji. Czasami może być trudno naprawić, gdy używasz słabo udokumentowanych bibliotek innych firm, ponieważ musisz znać zakres możliwych wartości, aby argumenty mogły przekazać poprawną wartość.
Oto niektóre z typowych scenariuszy, w których występuje RangeError:
- Próba utworzenia tablicy o niedozwolonych długościach za pomocą konstruktora Array.
- Przekazywanie złych wartości do metod numerycznych, takich jak
toExponential()
,toPrecision()
,toFixed()
, itp. - Przekazywanie niedozwolonych wartości do funkcji łańcuchowych, takich jak
normalize()
.
Błąd odniesienia
ReferenceError występuje, gdy coś jest nie tak z referencją zmiennej w kodzie. Być może zapomniałeś zdefiniować wartość zmiennej przed jej użyciem lub próbujesz użyć niedostępnej zmiennej w swoim kodzie. W każdym razie przeglądanie śladu stosu dostarcza wielu informacji, aby znaleźć i naprawić błędne odwołanie do zmiennej.
Oto niektóre z najczęstszych przyczyn wystąpienia błędów referencyjnych:
- Dokonywanie literówki w nazwie zmiennej.
- Próba uzyskania dostępu do zmiennych o zasięgu blokowym poza ich zasięgami.
- Odwoływanie się do zmiennej globalnej z zewnętrznej biblioteki (np. $ z jQuery) przed jej załadowaniem.
Błąd składni
Błędy te są jednymi z najprostszych do naprawienia, ponieważ wskazują na błąd w składni kodu. Ponieważ JavaScript jest językiem skryptowym, który jest interpretowany, a nie kompilowany, są one generowane, gdy aplikacja wykonuje skrypt zawierający błąd. W przypadku języków kompilowanych takie błędy są identyfikowane podczas kompilacji. W związku z tym pliki binarne aplikacji nie są tworzone, dopóki nie zostaną naprawione.
Oto niektóre z najczęstszych przyczyn występowania błędów składniowych:
- Brak cudzysłowów
- Brak nawiasów zamykających
- Niewłaściwe wyrównanie nawiasów klamrowych lub innych znaków
Dobrą praktyką jest użycie narzędzia lintingu w IDE, aby zidentyfikować takie błędy, zanim trafią one do przeglądarki.
Wpisz błąd
TypeError to jeden z najczęstszych błędów w aplikacjach JavaScript. Ten błąd powstaje, gdy jakaś wartość nie jest określonego oczekiwanego typu. Oto niektóre z najczęstszych przypadków, w których występuje:
- Wywoływanie obiektów, które nie są metodami.
- Próba uzyskania dostępu do właściwości obiektów zerowych lub niezdefiniowanych
- Traktowanie ciągu znaków jako liczby lub odwrotnie
Istnieje znacznie więcej możliwości wystąpienia błędu TypeError. Przyjrzymy się później niektórym znanym instancjom i dowiemy się, jak je naprawić.
Błąd wewnętrzny
Typ InternalError jest używany, gdy w silniku wykonawczym JavaScript wystąpi wyjątek. Może, ale nie musi, wskazywać na problem z Twoim kodem.
Najczęściej InternalError występuje tylko w dwóch scenariuszach:
- Gdy łatka lub aktualizacja środowiska wykonawczego JavaScript zawiera błąd powodujący zgłaszanie wyjątków (zdarza się to bardzo rzadko)
- Gdy twój kod zawiera encje, które są zbyt duże dla silnika JavaScript (np. zbyt wiele przypadków przełączania, zbyt duże inicjatory tablicy, zbyt duża rekurencja)
Najbardziej odpowiednim podejściem do rozwiązania tego błędu jest zidentyfikowanie przyczyny za pomocą komunikatu o błędzie i, jeśli to możliwe, zmiana struktury logiki aplikacji, aby wyeliminować nagły wzrost obciążenia silnika JavaScript.
Błąd URI
URIError występuje, gdy globalna funkcja obsługi identyfikatora URI, taka jak decodeURIComponent
, jest używana nielegalnie. Zwykle wskazuje, że parametr przekazany do wywołania metody nie był zgodny ze standardami URI, a zatem nie został poprawnie przeanalizowany przez metodę.
Diagnozowanie tych błędów jest zwykle łatwe, ponieważ wystarczy przeanalizować argumenty przemawiające za wadami rozwojowymi.
Błąd oceny
EvalError występuje, gdy wystąpi błąd podczas wywołania funkcji eval()
. Funkcja eval()
służy do wykonywania kodu JavaScript zapisanego w ciągach. Jednakże, ponieważ korzystanie z funkcji eval()
jest wysoce odradzane ze względu na problemy z bezpieczeństwem, a obecne specyfikacje ECMAScript nie generują już klasy EvalError
, ten typ błędu istnieje po prostu w celu zachowania wstecznej kompatybilności ze starszym kodem JavaScript.
Jeśli pracujesz na starszej wersji JavaScript, możesz napotkać ten błąd. W każdym razie najlepiej zbadać kod wykonywany w wywołaniu funkcji eval()
pod kątem wyjątków.
Tworzenie niestandardowych typów błędów
Chociaż JavaScript oferuje odpowiednią listę klas typów błędów, aby uwzględnić większość scenariuszy, zawsze możesz utworzyć nowy typ błędu, jeśli lista nie spełnia twoich wymagań. Podstawą tej elastyczności jest fakt, że JavaScript umożliwia dosłownie rzucanie czegokolwiek za pomocą polecenia throw
.
Z technicznego punktu widzenia te oświadczenia są całkowicie legalne:
throw 8 throw "An error occurred"
Jednak zgłaszanie pierwotnego typu danych nie zapewnia szczegółowych informacji o błędzie, takich jak jego typ, nazwa lub towarzyszący ślad stosu. Aby rozwiązać ten problem i ustandaryzować proces obsługi błędów, udostępniono klasę Error
. Odradza się również używanie prymitywnych typów danych podczas zgłaszania wyjątków.
Możesz rozszerzyć klasę Error
, aby utworzyć niestandardową klasę błędu. Oto podstawowy przykład tego, jak możesz to zrobić:
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }
Możesz go użyć w następujący sposób:
throw ValidationError("Property not found: name")
A następnie możesz go zidentyfikować za pomocą słowa kluczowego instanceof
:
try { validateForm() // code that throws a ValidationError } catch (e) { if (e instanceof ValidationError) // do something else // do something else }
10 najczęstszych błędów w JavaScript
Teraz, gdy znasz już najczęstsze typy błędów i wiesz, jak tworzyć własne, nadszedł czas, aby przyjrzeć się najczęstszym błędom, z którymi możesz się spotkać podczas pisania kodu JavaScript.
1. Niewyłapany błąd zakresu
Ten błąd występuje w przeglądarce Google Chrome w kilku różnych scenariuszach. Po pierwsze, może się to zdarzyć, jeśli wywołasz funkcję rekurencyjną, która się nie zakończy. Możesz to sprawdzić samodzielnie w Konsoli programisty Chrome:

Aby rozwiązać taki błąd, upewnij się, że poprawnie zdefiniowałeś przypadki graniczne funkcji rekurencyjnej. Innym powodem wystąpienia tego błędu jest przekazanie wartości spoza zakresu parametru funkcji. Oto przykład:

Komunikat o błędzie zwykle wskazuje, co jest nie tak z twoim kodem. Gdy wprowadzisz zmiany, zostanie to rozwiązane.

2. Niewyłapany błąd typu: nie można ustawić właściwości
Ten błąd występuje, gdy ustawisz właściwość w niezdefiniowanym odwołaniu. Możesz odtworzyć problem za pomocą tego kodu:
var list list.count = 0
Oto wynik, który otrzymasz:

Aby naprawić ten błąd, zainicjuj odwołanie wartością przed uzyskaniem dostępu do jego właściwości. Oto jak to wygląda po naprawie:

3. Nieprzechwycony błąd typu: Nie można odczytać właściwości
To jeden z najczęściej występujących błędów w JavaScript. Ten błąd występuje, gdy próbujesz odczytać właściwość lub wywołać funkcję na niezdefiniowanym obiekcie. Możesz go bardzo łatwo odtworzyć, uruchamiając następujący kod w konsoli programisty Chrome:
var func func.call()
Oto wynik:

Niezdefiniowany obiekt to jedna z wielu możliwych przyczyn tego błędu. Inną istotną przyczyną tego problemu może być nieprawidłowa inicjalizacja stanu podczas renderowania interfejsu użytkownika. Oto przykład ze świata rzeczywistego z aplikacji React:
import React, { useState, useEffect } from "react"; const CardsList = () => { const [state, setState] = useState(); useEffect(() => { setTimeout(() => setState({ items: ["Card 1", "Card 2"] }), 2000); }, []); return ( <> {state.items.map((item) => ( <li key={item}>{item}</li> ))} </> ); }; export default CardsList;
Aplikacja uruchamia się z pustym kontenerem stanu i jest dostarczana z niektórymi elementami po opóźnieniu 2 sekund. Opóźnienie jest wprowadzane, aby imitować połączenie sieciowe. Nawet jeśli Twoja sieć jest superszybka, nadal będziesz mieć do czynienia z niewielkim opóźnieniem, z powodu którego komponent zostanie wyrenderowany przynajmniej raz. Jeśli spróbujesz uruchomić tę aplikację, otrzymasz następujący błąd:

Dzieje się tak, ponieważ w momencie renderowania kontener stanu jest niezdefiniowany; w związku z tym nie ma na nim żadnych items
własności. Naprawienie tego błędu jest łatwe. Wystarczy podać początkową wartość domyślną kontenerowi stanu.
// ... const [state, setState] = useState({items: []}); // ...
Teraz, po ustawionym opóźnieniu, Twoja aplikacja pokaże podobny wynik:

Dokładna poprawka w kodzie może być inna, ale istotą jest tutaj zawsze prawidłowe inicjowanie zmiennych przed ich użyciem.
4. TypeError: „undefined” nie jest obiektem
Ten błąd występuje w przeglądarce Safari podczas próby uzyskania dostępu do właściwości lub wywołania metody na niezdefiniowanym obiekcie. Możesz uruchomić ten sam kod z góry, aby samodzielnie odtworzyć błąd.

Rozwiązanie tego błędu jest również takie samo — upewnij się, że poprawnie zainicjalizowałeś zmienne i nie są one niezdefiniowane podczas uzyskiwania dostępu do właściwości lub metody.
5. TypeError: null nie jest obiektem
To znowu jest podobne do poprzedniego błędu. Występuje w Safari, a jedyną różnicą między tymi dwoma błędami jest to, że ten jest generowany, gdy obiekt, którego właściwość lub metoda jest uzyskiwana, ma null
zamiast undefined
. Możesz to odtworzyć, uruchamiając następujący fragment kodu:
var func = null func.call()
Oto wynik, który otrzymasz:

Ponieważ null
to wartość jawnie ustawiona na zmienną i nie przypisywana automatycznie przez JavaScript. Ten błąd może wystąpić tylko wtedy, gdy próbujesz uzyskać dostęp do zmiennej, którą samodzielnie ustawiłeś jako null
. Musisz więc ponownie przyjrzeć się swojemu kodowi i sprawdzić, czy napisana przez Ciebie logika jest poprawna, czy nie.
6. TypeError: Nie można odczytać właściwości „length”
Ten błąd występuje w przeglądarce Chrome, gdy próbujesz odczytać długość obiektu null
lub undefined
. Przyczyna tego problemu jest podobna do poprzednich, ale występuje dość często podczas obsługi list; dlatego zasługuje na szczególną wzmiankę. Oto jak możesz odtworzyć problem:

Jednak w nowszych wersjach przeglądarki Chrome ten błąd jest zgłaszany jako Uncaught TypeError: Cannot read properties of undefined
. Tak to wygląda teraz:

Poprawka ponownie polega na upewnieniu się, że obiekt, do którego próbujesz uzyskać dostęp, istnieje i nie jest ustawiony na null
.
7. TypeError: „undefined” nie jest funkcją
Ten błąd występuje, gdy próbujesz wywołać metodę, która nie istnieje w twoim skrypcie lub istnieje, ale nie można się do niej odwoływać w kontekście wywołania. Ten błąd zwykle występuje w Google Chrome i możesz go rozwiązać, sprawdzając wiersz kodu zgłaszający błąd. Jeśli znajdziesz literówkę, napraw ją i sprawdź, czy to rozwiązuje Twój problem.
Jeśli w kodzie użyłeś słowa kluczowego this
, odwołującego się do siebie, ten błąd może wystąpić, jeśli this
jest on odpowiednio powiązany z kontekstem. Rozważ następujący kod:
function showAlert() { alert("message here") } document.addEventListener("click", () => { this.showAlert(); })
Jeśli wykonasz powyższy kod, zwróci omówiony przez nas błąd. Dzieje się tak, ponieważ funkcja anonimowa przekazana jako detektor zdarzeń jest wykonywana w kontekście document
.
Natomiast funkcja showAlert
jest zdefiniowana w kontekście window
.
Aby rozwiązać ten problem, musisz przekazać odpowiednią referencję do funkcji, wiążąc ją za pomocą metody bind()
:
document.addEventListener("click", this.showAlert.bind(this))
8. ReferenceError: zdarzenie nie jest zdefiniowane
Ten błąd występuje, gdy próbujesz uzyskać dostęp do odwołania niezdefiniowanego w zakresie wywołującym. Zwykle dzieje się tak podczas obsługi zdarzeń, ponieważ często dostarczają one odniesienia o nazwie event
w funkcji zwrotnej. Ten błąd może wystąpić, jeśli zapomnisz zdefiniować argument zdarzenia w parametrach funkcji lub go przeliterujesz.
Ten błąd może nie wystąpić w przeglądarce Internet Explorer lub Google Chrome (ponieważ IE oferuje globalną zmienną zdarzenia, a Chrome automatycznie dołącza zmienną zdarzenia do modułu obsługi), ale może wystąpić w przeglądarce Firefox. Warto więc uważać na takie drobne błędy.

9. TypeError: Przypisanie do zmiennej stałej
To błąd, który wynika z niedbalstwa. Jeśli spróbujesz przypisać nową wartość do zmiennej stałej, otrzymasz taki wynik:

Chociaż w tej chwili wydaje się to łatwe do naprawienia, wyobraź sobie setki takich deklaracji zmiennych i jedną z nich błędnie zdefiniowaną jako const
zamiast let
! W przeciwieństwie do innych języków skryptowych, takich jak PHP, istnieje minimalna różnica między stylem deklarowania stałych i zmiennych w JavaScript. Dlatego dobrze jest najpierw sprawdzić swoje deklaracje, gdy napotkasz ten błąd. Możesz również napotkać ten błąd, jeśli zapomnisz , że wspomniana referencja jest stałą i użyjesz jej jako zmiennej. Wskazuje to na nieostrożność lub błąd w logice aplikacji. Pamiętaj, aby to sprawdzić, próbując rozwiązać ten problem.
10. (nieznane): Błąd skryptu
Błąd skryptu występuje, gdy skrypt innej firmy wysyła błąd do przeglądarki. Po tym błędzie występuje (nieznane), ponieważ skrypt innej firmy należy do innej domeny niż Twoja aplikacja. Przeglądarka ukrywa inne szczegóły, aby zapobiec wyciekowi poufnych informacji ze skryptu innej firmy.
Nie możesz rozwiązać tego błędu bez znajomości wszystkich szczegółów. Oto, co możesz zrobić, aby uzyskać więcej informacji o błędzie:
- Dodaj atrybut
crossorigin
w tagu script. - Ustaw poprawny nagłówek
Access-Control-Allow-Origin
na serwerze hostującym skrypt. - [Opcjonalnie] Jeśli nie masz dostępu do serwera obsługującego skrypt, możesz rozważyć użycie serwera proxy do przekazywania żądania do serwera iz powrotem do klienta z poprawnymi nagłówkami.
Gdy uzyskasz dostęp do szczegółów błędu, możesz następnie rozwiązać problem, który prawdopodobnie będzie dotyczył biblioteki innej firmy lub sieci.
Jak identyfikować i zapobiegać błędom w JavaScript?
Chociaż omówione powyżej błędy są najczęstsze i najczęstsze w JavaScript, natkniesz się na kilka przykładów, które nigdy nie będą wystarczające. Ważne jest, aby zrozumieć, jak wykrywać i zapobiegać wszelkiego rodzaju błędom w aplikacji JavaScript podczas jej tworzenia. Oto jak radzić sobie z błędami w JavaScript.
Ręczne rzucanie i łapanie błędów
Najbardziej podstawowym sposobem obsługi błędów, które zostały zgłoszone ręcznie lub przez środowisko wykonawcze, jest ich przechwycenie. Podobnie jak większość innych języków, JavaScript oferuje zestaw słów kluczowych do obsługi błędów. Bardzo ważne jest dokładne poznanie każdego z nich, zanim przystąpisz do obsługi błędów w aplikacji JavaScript.
rzucić
Pierwszym i najbardziej podstawowym słowem kluczowym zestawu jest throw
. Jak widać, słowo kluczowe throw służy do ręcznego zgłaszania błędów w celu ręcznego tworzenia wyjątków w środowisku uruchomieniowym JavaScript. Omówiliśmy to już wcześniej w artykule, a oto sedno znaczenia tego słowa kluczowego:
- Możesz
throw
wszystko, w tym liczby, ciągi i obiektyError
. - Jednak nie zaleca się rzucania prymitywnych typów danych, takich jak ciągi i liczby, ponieważ nie zawierają one informacji debugowania o błędach.
- Przykład:
throw TypeError("Please provide a string")
próbować
Słowo kluczowe try
służy do wskazania, że blok kodu może zgłosić wyjątek. Jego składnia to:
try { // error-prone code here }
Należy zauważyć, że blok catch
musi zawsze następować po bloku try
, aby skutecznie obsługiwać błędy.
łapać
Słowo kluczowe catch
służy do tworzenia bloku catch. Ten blok kodu jest odpowiedzialny za obsługę błędów, które przechwytuje końcowy blok try
. Oto jego składnia:
catch (exception) { // code to handle the exception here }
W ten sposób zaimplementujesz razem bloki try
i catch
:
try { // business logic code } catch (exception) { // error handling code }
W przeciwieństwie do C++ lub Javy, w JavaScript nie można dołączać wielu bloków catch
do bloku try
. Oznacza to, że nie możesz tego zrobić:
try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } } catch (exception) { if (exception instanceof RangeError) { // do something } }
Zamiast tego można użyć instrukcji if...else
lub instrukcji switch case wewnątrz pojedynczego bloku catch, aby obsłużyć wszystkie możliwe przypadki błędów. Wyglądałoby to tak:
try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } else if (exception instanceof RangeError) { // do something else } }
wreszcie
finally
kluczowe last służy do definiowania bloku kodu, który jest uruchamiany po obsłużeniu błędu. Ten blok jest wykonywany po blokach try i catch.
Również ostatni blok zostanie wykonany niezależnie od wyniku pozostałych dwóch bloków. Oznacza to, że nawet jeśli blok catch nie może całkowicie obsłużyć błędu lub zostanie zgłoszony błąd w bloku catch, interpreter wykona kod w bloku finally przed awarią programu.
Aby zostać uznanym za prawidłowy, po bloku try w JavaScript musi następować albo catch, albo blok finally. Bez żadnego z nich interpreter zgłosi SyntaxError. Dlatego pamiętaj, aby podczas obsługi błędów postępować zgodnie z blokami prób z co najmniej jednym z nich.
Obsługuj błędy globalnie za pomocą metody onerror()
Metoda onerror()
jest dostępna dla wszystkich elementów HTML do obsługi wszelkich błędów, które mogą z nimi wystąpić. Na przykład, jeśli tag img
nie może znaleźć obrazu, którego URL jest określony, uruchamia metodę onerror, aby umożliwić użytkownikowi obsłużenie błędu.
Zazwyczaj w wywołaniu onerror podaje się inny adres URL obrazu, do którego ma powrócić tag img
. Oto jak możesz to zrobić za pomocą JavaScript:
const image = document.querySelector("img") image.onerror = (event) => { console.log("Error occurred: " + event) }
Możesz jednak użyć tej funkcji, aby utworzyć globalny mechanizm obsługi błędów dla swojej aplikacji. Oto jak możesz to zrobić:
window.onerror = (event) => { console.log("Error occurred: " + event) }
Dzięki tej obsłudze zdarzeń możesz pozbyć się wielu bloków try...catch
znajdujących się w kodzie i scentralizować obsługę błędów aplikacji, podobnie jak obsługa zdarzeń. Do okna można dołączyć wiele programów obsługi błędów, aby zachować zasadę pojedynczej odpowiedzialności z zasad projektowania SOLID. Interpreter będzie przechodził przez wszystkie programy obsługi, aż dotrze do właściwego.
Przekaż błędy przez wywołania zwrotne
Podczas gdy proste i liniowe funkcje pozwalają na prostą obsługę błędów, wywołania zwrotne mogą skomplikować sprawę.
Rozważ następujący fragment kodu:
Potrzebujesz rozwiązania hostingowego, które zapewni Ci przewagę nad konkurencją? Kinsta zapewnia niesamowitą szybkość, najnowocześniejsze zabezpieczenia i automatyczne skalowanie. Sprawdź nasze plany
const calculateCube = (number, callback) => { setTimeout(() => { const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) calculateCube(4, callback)
Powyższa funkcja demonstruje asynchroniczny stan, w którym funkcja zajmuje trochę czasu na przetworzenie operacji i zwraca wynik później za pomocą wywołania zwrotnego.
Jeśli spróbujesz wprowadzić ciąg zamiast 4 w wywołaniu funkcji, w rezultacie otrzymasz NaN
.
To musi być właściwie obsługiwane. Oto jak:
const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") throw new Error("Numeric argument is expected") const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) try { calculateCube(4, callback) } catch (e) { console.log(e) }
Powinno to idealnie rozwiązać problem. Jeśli jednak spróbujesz przekazać ciąg do wywołania funkcji, otrzymasz to:

Mimo że zaimplementowałeś blok try-catch podczas wywoływania funkcji, nadal mówi, że błąd nie został przechwycony. Błąd jest zgłaszany po wykonaniu bloku catch z powodu przekroczenia limitu czasu.
Może to nastąpić szybko w połączeniach sieciowych, w których pojawiają się nieoczekiwane opóźnienia. Takie przypadki należy uwzględnić podczas tworzenia aplikacji.
Oto jak prawidłowo obsłużyć błędy w wywołaniach zwrotnych:
const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") { callback(new TypeError("Numeric argument is expected")) return } const cube = number * number * number callback(null, cube) }, 2000) } const callback = (error, result) => { if (error !== null) { console.log(error) return } console.log(result) } try { calculateCube('hey', callback) } catch (e) { console.log(e) }
Teraz dane wyjściowe w konsoli będą wyglądały następująco:

Oznacza to, że błąd został odpowiednio obsłużony.
Obsługuj błędy w obietnicach
Większość ludzi woli obietnice obsługi działań asynchronicznych. Obietnice mają jeszcze jedną zaletę — odrzucona obietnica nie kończy twojego skryptu. Jednak nadal musisz zaimplementować blok catch, aby obsłużyć błędy w obietnicach. Aby lepiej to zrozumieć, przepiszmy funkcję calculateCube()
za pomocą Obietnic:
const delay = ms => new Promise(res => setTimeout(res, ms)); const calculateCube = async (number) => { if (typeof number !== "number") throw Error("Numeric argument is expected") await delay(5000) const cube = number * number * number return cube } try { calculateCube(4).then(r => console.log(r)) } catch (e) { console.log(e) }
Limit czasu z poprzedniego kodu został wyizolowany w funkcji delay
w celu zrozumienia. Jeśli spróbujesz wpisać ciąg zamiast 4, wynik, który otrzymasz, będzie podobny do tego:

Ponownie, jest to spowodowane tym, że Promise
rzuca błąd po zakończeniu wykonywania wszystkich innych elementów. Rozwiązanie tego problemu jest proste. Po prostu dodaj wywołanie catch()
do łańcucha obietnic w następujący sposób:
calculateCube("hey") .then(r => console.log(r)) .catch(e => console.log(e))
Teraz dane wyjściowe będą następujące:

Możesz zaobserwować, jak łatwo radzić sobie z błędami za pomocą obietnic. Dodatkowo możesz połączyć blok finally()
i wywołanie obietnicy, aby dodać kod, który zostanie uruchomiony po zakończeniu obsługi błędów.
Alternatywnie możesz również obsłużyć błędy w obietnicach za pomocą tradycyjnej techniki try-catch-finally. Oto jak wyglądałoby Twoje wezwanie do przyrzeczenia w takim przypadku:
try { let result = await calculateCube("hey") console.log(result) } catch (e) { console.log(e) } finally { console.log('Finally executed") }
Działa to jednak tylko wewnątrz funkcji asynchronicznej. Dlatego najbardziej preferowanym sposobem obsługi błędów w obietnicach jest łańcuch catch
i finally
wywołanie obietnicy.
throw/catch vs onerror() vs Callbacks vs Promises: który jest najlepszy?
Mając do dyspozycji cztery metody, musisz wiedzieć, jak wybrać najbardziej odpowiednią w danym przypadku użycia. Oto jak możecie sami zdecydować:
rzucić/złapać
Będziesz używać tej metody przez większość czasu. Upewnij się, że zaimplementowałeś warunki dla wszystkich możliwych błędów w bloku catch i pamiętaj o dołączeniu bloku finally, jeśli chcesz uruchomić kilka procedur czyszczenia pamięci po bloku try.
Jednak zbyt wiele bloków try/catch może utrudnić utrzymanie kodu. Jeśli znajdziesz się w takiej sytuacji, możesz chcieć obsłużyć błędy za pomocą globalnego programu obsługi lub metody obiecanej.
Decydując między asynchronicznymi blokami try/catch a catch()
obietnicy, zaleca się skorzystanie z asynchronicznych bloków try/catch, ponieważ sprawią, że kod będzie liniowy i łatwy do debugowania.
błąd ()
Najlepiej używać metody onerror()
, gdy wiesz, że Twoja aplikacja musi obsługiwać wiele błędów i mogą one być dobrze rozrzucone po całym kodzie. Metoda onerror
umożliwia obsługę błędów tak, jakby były one kolejnym zdarzeniem obsługiwanym przez aplikację. Możesz zdefiniować wiele programów obsługi błędów i dołączyć je do okna aplikacji podczas początkowego renderowania.
Należy jednak pamiętać, że metoda onerror()
może być niepotrzebnie trudna do skonfigurowania w mniejszych projektach z mniejszym zakresem błędów. Jeśli masz pewność, że Twoja aplikacja nie wygeneruje zbyt wielu błędów, najlepiej sprawdzi się tradycyjna metoda throw/catch.
Oddzwonienia i obietnice
Obsługa błędów w wywołaniach zwrotnych i obietnicach różni się ze względu na projekt i strukturę kodu. Jeśli jednak wybierzesz między tymi dwoma, zanim napiszesz swój kod, najlepiej będzie iść z obietnicami.
Dzieje się tak, ponieważ obietnice mają wbudowaną konstrukcję do łączenia w łańcuchy bloków catch()
i finally()
w celu łatwej obsługi błędów. This method is easier and cleaner than defining additional arguments/reusing existing arguments to handle errors.
Keep Track of Changes With Git Repositories
Many errors often arise due to manual mistakes in the codebase. While developing or debugging your code, you might end up making unnecessary changes that may cause new errors to appear in your codebase. Automated testing is a great way to keep your code in check after every change. However, it can only tell you if something's wrong. If you don't take frequent backups of your code, you'll end up wasting time trying to fix a function or a script that was working just fine before.
This is where git plays its role. With a proper commit strategy, you can use your git history as a backup system to view your code as it evolved through the development. You can easily browse through your older commits and find out the version of the function working fine before but throwing errors after an unrelated change.
You can then restore the old code or compare the two versions to determine what went wrong. Modern web development tools like GitHub Desktop or GitKraken help you to visualize these changes side by side and figure out the mistakes quickly.
A habit that can help you make fewer errors is running code reviews whenever you make a significant change to your code. If you're working in a team, you can create a pull request and have a team member review it thoroughly. This will help you use a second pair of eyes to spot out any errors that might have slipped by you.
Best Practices for Handling Errors in JavaScript
The above-mentioned methods are adequate to help you design a robust error handling approach for your next JavaScript application. However, it would be best to keep a few things in mind while implementing them to get the best out of your error-proofing. Here are some tips to help you.
1. Use Custom Errors When Handling Operational Exceptions
We introduced custom errors early in this guide to give you an idea of how to customize the error handling to your application's unique case. It's advisable to use custom errors wherever possible instead of the generic Error
class as it provides more contextual information to the calling environment about the error.
On top of that, custom errors allow you to moderate how an error is displayed to the calling environment. This means that you can choose to hide specific details or display additional information about the error as and when you wish.
You can go so far as to format the error contents according to your needs. This gives you better control over how the error is interpreted and handled.
2. Do Not Swallow Any Exceptions
Even the most senior developers often make a rookie mistake — consuming exceptions levels deep down in their code.
You might come across situations where you have a piece of code that is optional to run. If it works, great; if it doesn't, you don't need to do anything about it.
In these cases, it's often tempting to put this code in a try block and attach an empty catch block to it. However, by doing this, you'll leave that piece of code open to causing any kind of error and getting away with it. This can become dangerous if you have a large codebase and many instances of such poor error management constructs.
The best way to handle exceptions is to determine a level on which all of them will be dealt and raise them until there. This level can be a controller (in an MVC architecture app) or a middleware (in a traditional server-oriented app).
This way, you'll get to know where you can find all the errors occurring in your app and choose how to resolve them, even if it means not doing anything about them.
3. Use a Centralized Strategy for Logs and Error Alerts
Logging an error is often an integral part of handling it. Those who fail to develop a centralized strategy for logging errors may miss out on valuable information about their app's usage.
An app's event logs can help you figure out crucial data about errors and help to debug them quickly. If you have proper alerting mechanisms set up in your app, you can know when an error occurs in your app before it reaches a large section of your user base.
It's advisable to use a pre-built logger or create one to suit your needs. You can configure this logger to handle errors based on their levels (warning, debug, info, etc.), and some loggers even go so far as to send logs to remote logging servers immediately. This way, you can watch how your application's logic performs with active users.
4. Notify Users About Errors Appropriately
Another good point to keep in mind while defining your error handling strategy is to keep the user in mind.
All errors that interfere with the normal functioning of your app must present a visible alert to the user to notify them that something went wrong so the user can try to work out a solution. If you know a quick fix for the error, such as retrying an operation or logging out and logging back in, make sure to mention it in the alert to help fix the user experience in real-time.
In the case of errors that don't cause any interference with the everyday user experience, you can consider suppressing the alert and logging the error to a remote server for resolving later.
5. Implement a Middleware (Node.js)
The Node.js environment supports middlewares to add functionalities to server applications. You can use this feature to create an error-handling middleware for your server.
The most significant benefit of using middleware is that all of your errors are handled centrally in one place. You can choose to enable/disable this setup for testing purposes easily.
Here's how you can create a basic middleware:
const logError = err => { console.log("ERROR: " + String(err)) } const errorLoggerMiddleware = (err, req, res, next) => { logError(err) next(err) } const returnErrorMiddleware = (err, req, res, next) => { res.status(err.statusCode || 500) .send(err.message) } module.exports = { logError, errorLoggerMiddleware, returnErrorMiddleware }
Następnie możesz użyć tego oprogramowania pośredniczącego w swojej aplikacji w następujący sposób:
const { errorLoggerMiddleware, returnErrorMiddleware } = require('./errorMiddleware') app.use(errorLoggerMiddleware) app.use(returnErrorMiddleware)
Możesz teraz zdefiniować niestandardową logikę w oprogramowaniu pośredniczącym, aby odpowiednio obsługiwać błędy. Nie musisz już martwić się implementacją poszczególnych konstrukcji obsługi błędów w całej bazie kodu.
6. Uruchom ponownie swoją aplikację, aby poradzić sobie z błędami programisty (Node.js)
Gdy aplikacje Node.js napotkają błędy programisty, niekoniecznie muszą zgłosić wyjątek i spróbować zamknąć aplikację. Takie błędy mogą obejmować problemy wynikające z błędów programisty, takie jak wysokie zużycie procesora, rozdęcie pamięci lub wycieki pamięci. Najlepszym sposobem radzenia sobie z nimi jest łagodne ponowne uruchomienie aplikacji przez awarię jej za pomocą trybu klastra Node.js lub unikalnego narzędzia, takiego jak PM2. Może to zapewnić, że aplikacja nie ulegnie awarii podczas działania użytkownika, prezentując okropne wrażenia użytkownika.
7. Złap wszystkie niezłapane wyjątki (Node.js)
Nigdy nie możesz być pewien, że uwzględniłeś wszystkie możliwe błędy, które mogą wystąpić w Twojej aplikacji. Dlatego konieczne jest zaimplementowanie strategii awaryjnej, aby przechwycić wszystkie nieprzechwycone wyjątki z aplikacji.
Oto jak możesz to zrobić:
process.on('uncaughtException', error => { console.log("ERROR: " + String(error)) // other handling mechanisms })
Możesz również określić, czy błąd, który wystąpił, jest standardowym wyjątkiem, czy niestandardowym błędem operacyjnym. Na podstawie wyniku możesz wyjść z procesu i ponownie go uruchomić, aby uniknąć nieoczekiwanego zachowania.
8. Złap wszystkie nieobsłużone odrzucenia obietnic (Node.js)
Podobnie jak w przypadku, gdy nigdy nie możesz zakryć wszystkich możliwych wyjątków, istnieje duża szansa, że możesz przegapić obsługę wszystkich możliwych odrzuceń obietnic. Jednak w przeciwieństwie do wyjątków odrzucenie obietnicy nie powoduje błędów.
Tak więc ważna obietnica, która została odrzucona, może prześlizgnąć się jako ostrzeżenie i narazić aplikację na nieoczekiwane zachowanie. Dlatego ważne jest, aby wdrożyć mechanizm awaryjny do obsługi odrzucenia obietnicy.
Oto jak możesz to zrobić:
const promiseRejectionCallback = error => { console.log("PROMISE REJECTED: " + String(error)) } process.on('unhandledRejection', callback)
tweetować Streszczenie
Jak każdy inny język programowania, w JavaScript błędy są dość częste i naturalne. W niektórych przypadkach może być konieczne celowe zgłaszanie błędów, aby wskazać użytkownikom poprawną odpowiedź. Dlatego zrozumienie ich anatomii i typów jest bardzo ważne.
Co więcej, musisz być wyposażony w odpowiednie narzędzia i techniki, aby identyfikować i zapobiegać błędom, które mogą spowodować usunięcie Twojej aplikacji.
W większości przypadków solidna strategia obsługi błędów z ostrożnym wykonaniem jest wystarczająca dla wszystkich typów aplikacji JavaScript.
Czy są jakieś inne błędy JavaScript, których nadal nie udało się rozwiązać? Jakieś techniki konstruktywnej obsługi błędów JS? Daj nam znać w komentarzach poniżej!