• Programowanie
  • try-catch w praktyce - jak obsługiwać wyjątki bez ukrywania błędów

try-catch w praktyce - jak obsługiwać wyjątki bez ukrywania błędów

Radosław Stępień • 8 listopada 2026
Abstrakcyjny obraz z elementami interfejsu użytkownika, wykresami i ikonami. Ludzie pracują przy komputerach, próbując złapać pomysły i rozwiązać problemy.

Spis treści

Żądanie do API kończy się błędem, a aplikacja zamiast wyświetlić użytkownikowi sensowny komunikat nagle przerywa działanie. Blok try-catch pozwala przejąć kontrolę nad takim wyjątkiem, o ile kod w catch naprawdę potrafi obsłużyć problem, a nie tylko go ukrywa.

Obsługuj wyjątek tylko wtedy, gdy wiesz, co zrobić dalej

  • try obejmuje fragment, w którym może wystąpić wyjątek.
  • catch powinien rozpoznawać błąd i podjąć działanie albo przekazać go dalej.
  • finally służy do sprzątania, a nie do obsługi błędu.
  • W JavaScript odrzucona obietnica trafia do try-catch tylko wtedy, gdy ją obsłużysz, na przykład przez await.

Blok catch ma sens tylko wtedy, gdy zmienia dalszy przebieg programu

Wyjątek przerywa zwykły przepływ instrukcji i szuka pasującej obsługi. Gdy program ją znajdzie, wykonuje kod catch; jeśli nie, błąd może propagować się wyżej, aż do miejsca, które potrafi zareagować. Samo złapanie wyjątku nie naprawia problemu.

Przykład w JavaScript pokazuje właściwy cel: zamienić techniczny błąd odczytu danych na kontrolowany rezultat dla aplikacji.

async function pobierzProfil(id) {
  try {
    const odpowiedz = await fetch(`/api/profiles/${id}`);

    if (!odpowiedz.ok) {
      throw new Error(`HTTP ${odpowiedz.status}`);
    }

    return await odpowiedz.json();
  } catch (error) {
    console.error("Nie udało się pobrać profilu:", error);
    return null;
  }
}

W takim wariancie funkcja zwraca null, więc jej kod wywołujący musi umieć obsłużyć brak profilu. Jeśli zwrócenie wartości zastępczej prowadziłoby do błędnych danych albo ukrywało awarię, zamiast tego wyjątek powinien zostać ponownie zgłoszony. Obsługa jest dobra dopiero wtedy, gdy dalszy stan programu pozostaje zrozumiały.

Łap tylko błędy, na które masz konkretną reakcję

Najczęstsza pułapka jest kusząco prosta: objąć dużą część funkcji jednym try i przechwycić każdy wyjątek. Wtedy pojedynczy handler może ukryć zarówno spodziewany brak pliku, jak i błąd programistyczny, którego nie powinno się traktować jak zwykłego przypadku.

Przydatna reguła brzmi: jeśli potrafisz wskazać konkretną reakcję na dany typ błędu, przechwytuj go lokalnie; jeśli nie, pozwól mu dotrzeć do wyższej warstwy, która może zapisać kontekst i zdecydować o odpowiedzi. Ogólny handler na granicy aplikacji nadal może być potrzebny, ale nie powinien po cichu zamieniać każdej awarii w pozorny sukces.

Sytuacja Prawdopodobny skutek Co zrobić
Błąd sieciowy, a ekran ma pokazać komunikat i umożliwić ponowienie Użytkownik może dostać czytelną informację zamiast przerwanego widoku Przechwyć spodziewany błąd i ustaw stan interfejsu na „nie udało się pobrać danych”
Pusty blok catch lub samo logowanie bez dalszej decyzji Program może działać dalej, choć wynik operacji jest nieznany Przywróć bezpieczny stan, zwróć jawny błąd albo przekaż wyjątek wyżej
W wielu miejscach pojawia się catch (Exception) Różne błędy trafiają do tej samej, często zbyt ogólnej obsługi Zawęź typ wyjątku; szeroki handler zostaw dla granicy procesu lub aplikacji

Badanie analizujące ponad 10 tys. bloków obsługi wyjątków i ponad 77 tys. przepływów wyjątków w 16 projektach Java i C# wykazało, że pojedynczy blok try może wiązać się nawet z 12 potencjalnie możliwymi wyjątkami, które dalej propagują się w programie. To dobry powód, by sprawdzać dokumentację wywoływanych metod zamiast zakładać, że ryzyko kończy się na widocznej linijce; opis badania znajdziesz w pracy Revisiting Exception Handling Practices with Exception Flow Analysis.

W JavaScript brak await może ominąć catch

Tu łatwo o błąd, który wygląda na działający kod. Blok try-catch przechwytuje synchroniczne wyjątki, ale przy operacjach asynchronicznych musi jeszcze dojść do faktycznego oczekiwania na wynik obietnicy. Dokumentacja MDN dotycząca funkcji async wyjaśnia, że await pozwala potraktować odrzucenie obietnicy jak wyjątek w tym bloku.

async function pobierzDane() {
  try {
    const dane = await pobierzZSerwera();
    return dane;
  } catch (error) {
    console.error("Pobieranie nie powiodło się:", error);
    return null;
  }
}

Jeśli usuniesz await, funkcja może zwrócić odrzuconą obietnicę, zanim błąd dotrze do tego catch. Wtedy obsługę trzeba dodać w łańcuchu, na przykład przez .catch(...), albo poczekać na wynik wewnątrz bloku. Sprawdź, czy błąd występuje podczas wywołania, czy dopiero po nim - od tego zależy, gdzie powinien znaleźć się handler.

Podobna ostrożność przydaje się przy wielu równoległych operacjach. Gdy każda ma zostać rozliczona osobno, dobierz sposób zbierania wyników do tego celu; sama lista wyrażeń await nie zawsze podłącza obsługę odrzuceń wszystkich obietnic w tym samym momencie.

finally sprząta, a w różnych językach robi to inaczej

W bloku finally umieszcza się czynności, które powinny wykonać się przy opuszczaniu konstrukcji, na przykład zwolnienie zasobu lub zakończenie stanu ładowania. W JavaScript działa on po try i catch, także wtedy, gdy przepływ wychodzi przez return. Uważam, że najczytelniej trzymać tam sprzątanie, a nie decyzje o tym, czy błąd uznać za obsłużony.

Składnia zależy od języka, a drobne różnice wpływają na poprawność kodu. W C# kolejność bloków catch ma znaczenie: środowisko sprawdza je od góry i wykona pierwszy pasujący handler. Z kolei Java oferuje try-with-resources, który automatycznie zamyka zasoby implementujące AutoCloseable; szczegóły opisuje dokumentacja Oracle Java Tutorials. W C# przykłady kolejności i ponownego zgłaszania wyjątku przedstawia dokumentacja Microsoft Learn.

Praktycznie oznacza to, że nie przenoś mechanicznie wzorca z jednego języka do drugiego. W Javie dla zasobu, który trzeba zamknąć, rozważ konstrukcję try-with-resources; w C# uporządkuj obsługę typów od bardziej szczegółowych do ogólnych; w JavaScript upewnij się, że odrzucenie obietnicy rzeczywiście trafia do handlera.

Przed dodaniem try-catch ustal, jaki stan ma przetrwać błąd

Najpierw odpowiedz sobie na jedno pytanie: co program ma zrobić, gdy ta operacja się nie powiedzie? Jeśli odpowiedź brzmi „pokazać komunikat i pozwolić ponowić”, lokalna obsługa może być właściwa. Jeśli nie ma bezpiecznej kontynuacji, przechwycenie wyjątku tylko opóźni moment, w którym problem stanie się widoczny.

Przy projektowaniu handlera nazwij typ błędu, zachowaj informacje przydatne do diagnozy i wybierz jawny rezultat albo ponowne zgłoszenie. Wtedy try-catch przestaje być siatką rozpiętą pod całym kodem, a staje się kontrolowanym punktem decyzji.

FAQ - Najczęstsze pytania

Przechwyć wyjątek lokalnie, gdy znasz konkretną reakcję, na przykład wyświetlenie komunikatu o błędzie sieci i umożliwienie ponowienia operacji. Jeśli zwrócenie wartości zastępczej, takiej jak null, mogłoby ukryć awarię lub doprowadzić do błędnych danych, ponownie zgłoś wyjątek albo pozwól mu dotrzeć do wyższej warstwy.

try-catch przechwytuje wyjątki synchroniczne, a odrzucenie obietnicy trafia do tego bloku wtedy, gdy kod faktycznie oczekuje na wynik przez await. Bez await funkcja może zwrócić odrzuconą obietnicę, zanim błąd zostanie obsłużony, dlatego trzeba użyć .catch(...) albo poczekać na wynik wewnątrz bloku.

finally służy do sprzątania, na przykład zwolnienia zasobu lub zakończenia stanu ładowania, i wykonuje się w JavaScript także wtedy, gdy przepływ opuszcza blok przez return. W Javie można użyć try-with-resources dla zasobów implementujących AutoCloseable, natomiast w C# należy między innymi uporządkować bloki catch od bardziej szczegółowych do ogólnych.

Pusty blok catch albo samo logowanie bez decyzji może pozwolić programowi działać dalej mimo nieznanego wyniku operacji. Szeroki handler może również ukryć błąd programistyczny razem ze spodziewanym przypadkiem, dlatego warto zawężać typ wyjątku i zostawiać ogólną obsługę głównie na granicy aplikacji lub procesu.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

wyjątki
javascript
asynchroniczność
obietnice
try-with-resources
Autor Radosław Stępień
Radosław Stępień
Dziedzina programowania i technologii rozwija się w zawrotnym tempie, co sprawia, że ciągłe śledzenie nowinek i głębokie zrozumienie procesów są kluczowe. Nazywam się Radosław Stępień i od 13 lat aktywnie działam w branży IT, dzieląc się swoją wiedzą i doświadczeniem. Moje zainteresowania skupiają się na praktycznych aspektach tworzenia oprogramowania, analizie trendów technologicznych oraz budowaniu satysfakcjonującej ścieżki kariery w tym dynamicznym sektorze. Staram się przedstawiać złożone zagadnienia w sposób przystępny, opierając się na sprawdzonych informacjach i własnych obserwacjach z wieloletniej pracy. Zależy mi na tym, aby treści, które tworzę, były nie tylko merytoryczne i aktualne, ale przede wszystkim pomocne dla każdego, kto chce zgłębić tajniki IT lub rozwijać się zawodowo.

Udostępnij artykuł

Napisz komentarz