try-catch w javascript… czy to nie jest dobra praktyka?

69

Istnieje przepis dotyczący blokowania try-catch w javascript . Podczas gdy w java lub innym języku obowiązkowa jest obsługa błędów, nie widzę, aby ktokolwiek używał ich w javascript w większym stopniu. Czy to nie jest dobra praktyka, czy po prostu nie potrzebujemy ich w javascript?

akp
źródło
2
While in java or *any other language* it is mandatory to have error handling...- Nie całkiem. Java, tak, ale istnieje wiele języków, które nie nalegają na try-catch (jak C #).
Jim G.
Jest tak, ponieważ nie można ich używać w środowisku asynchronicznym. Używam ich często, synchronizując kod o niższym poziomie abstrakcji, na przykład przekształcając coś w coś itp.
inf3rno
Założę się, że widzisz więcej try-catch w kodzie po stronie serwera niż po stronie klienta. Większość kodu klienckiego, do którego masz uprawnienia, nie robi nic ważnego. Zakładam, że minimalizacja KB w dół potoku jest ważniejsza niż odzyskiwanie po każdym błędzie - w większości aplikacji i okolicznościach po stronie przeglądarki.
svidgen
Jest jeszcze jeden sposób, aby uniknąć próby złapania za pomocą może i albo (monady) mieliśmy skrobak internetowy napisany w węźle. Byliśmy w stanie usunąć wszystkie try catch, używając go.
user93

Odpowiedzi:

66

Należy unikać throwbłędów jako sposobu przekazywania warunków błędów w aplikacjach.

throwOświadczenie powinno być stosowane jedynie „Do tego nigdy nie powinno się zdarzyć, crash i spalić. Nie elegancko odzyskać w jakikolwiek sposób”

try catch jest jednak stosowany w sytuacji, gdy obiekty hosta lub skrypt ECMAScript mogą zgłaszać błędy.

Przykład:

var json
try {
    json = JSON.parse(input)
} catch (e) {
    // invalid json input, set to null
    json = null
}

Zalecenia w społeczności node.js to przekazywanie błędów w wywołaniach zwrotnych (ponieważ błędy występują tylko w przypadku operacji asynchronicznych) jako pierwszy argument

fs.readFile(uri, function (err, fileData) {
    if (err) {
        // handle
        // A. give the error to someone else
        return callback(err)
        // B. recover logic
        return recoverElegantly(err)
        // C. Crash and burn
        throw err
    }
    // success case, handle nicely
})

Istnieją również inne problemy, takie jak try / catch jest naprawdę drogi i brzydki, a po prostu nie działa z operacjami asynchronicznymi.

Ponieważ operacje synchroniczne nie powinny generować błędów i nie działają z operacjami asynchronicznymi, nikt nie używa try catch, z wyjątkiem błędów zgłaszanych przez obiekty hosta lub skrypt ECMAScript

Raynos
źródło
2
Nie posunąłbym się nawet do stwierdzenia, że nikt nie używa try catch, w większości przypadków jest to po prostu złe narzędzie do tego zadania. Kiedy są naprawdę wyjątkowe okoliczności, warto rzucić Error, ale jest ich niewiele.
zzzzBov
7
@zzzzBov Nie ma nic złego w rzucaniu błędów w przypadku C, awarii i nagrania. Po prostu nie sądzę, że powinieneś łapać błędy i odzyskiwać. Na przykład document.getElementByIdnie rzuca, gdy element nie istnieje, po prostu zwraca null. To samo można zrobić dla prawie wszystkich przypadków
Raynos,
4
@Raynos, rzucanie z funkcji synchronizacji jest całkowicie dopuszczalne i ma sens w tym przypadku użycia. Zwracanie błędu do wywołania zwrotnego jest asynchroniczne, ponieważ błąd zgłaszenia jest synchronizowany.
sbartell
@Raynos mają jakieś dobre rady na temat unikania błędów / wyjątków do kontroli przepływu po stronie klienta (środowisko inne niż węzeł)? Znalazłem ten post na StackOverflow , ale jest on głównie ukierunkowany na Javę
blong
@ b. długie proste. nigdy nie rzucaj błędów. Problem rozwiązany.
Raynos 18.04.13
32

Try / catch w Javascript nie jest tak kuloodporny jak w innych językach z powodu asynchronicznej natury Javascript. Rozważ ten fragment:

try {
    setTimeout(function() {
        do_something_that_throws();
    }, 1000);
}
catch (e) {
    alert("You won't see this!");
}

Problem polega na tym, że przepływ sterowania opuszcza tryblok przed do_something_that_throws()wykonaniem, więc błąd zgłoszony w wywołaniu zwrotnym nigdy nie zostaje wyłapany.

Dlatego w wielu przypadkach try / catch jest w zasadzie nieodpowiednie i nie zawsze jest oczywiste, czy coś wykonuje kod asynchronicznie, czy nie. Na szczęście javascript ze swoistym idiomem jednowątkowym asynchronicznym zwrotnym i obsługa faktycznych zamknięć stanowi elegancką alternatywę: obsługę błędów w stylu kontynuacji przejścia. Wystarczy przekazać właściwą odpowiedź na każdy błąd jako funkcję, np .:

setTimeout(function () {
    do_something_that_calls_err(function(err) {
        alert("Something went wrong, namely this: " + err);
    }),
    1000);
tdammers
źródło
5
nie jest to również kuloodporne w innych językach. Przenieś ten kod na dowolny język obsługujący asynchroniczne wywołania zwrotne, a on również zawiedzie.
Raynos
2
@Raynos: Masz rację; jednak inne języki (a raczej ich kultury wspierające) nie kupują tego zbyt mocno w idiomie asynchronicznego wywołania zwrotnego, podobnie jak JavaScript, a ze względu na wielowątkową naturę większości innych środowisk, wady try / catch są bardziej oczywiste i otoczony wieloma innymi zastrzeżeniami, więc ludzie naturalnie stąpają ostrożnie w wielowątkowych / asynchronicznych sytuacjach oddzwaniania i są bardziej świadomi potencjalnych pułapek.
tdammers
Czy to naprawdę wynika z „asynchronicznej natury JavaScript”? O ile mi wiadomo, Try / Catch można teoretycznie sprawić, by działały leksykalnie, podobnie jak zamykanie leksykonów identyfikatorów; po prostu tak się nie dzieje w JavaScript.
Brian Gordon,
2
W node.js> = 0,8 warto zauważyć, że można spróbować / złapać asynchronicznie, proszę zobaczyć moje pytanie stackoverflow.com/questions/14301839/...
Benjamin Gruenbaum
Czy jedyną alternatywą dla synchronicznego try-catch jest asynchroniczny styl przekazywania kontynuacji?
CMCDragonkai
23

Wiele z tych odpowiedzi jest nieco starych i nie uwzględnia nowych funkcji ES7 asynci await.

Używając async/ awaitmożesz teraz uzyskać asynchroniczny przepływ sterowania, jak chcesz:

async function email(address) {
  try {
    // Do something asynchronous that may throw...
    await sendEmail({ to: address, from: '[email protected]`, subject: 'Hello' })
  } catch(err) {
    if (err instanceof SomeCustomError) {
      elegantlyHandleError(err)
    } else {
      throw err
    } 
  }
})

Dowiedz się więcej o async/ awaittutaj . Możesz użyć async/ awaitteraz używając babel .

Dana Woodman
źródło
Ale jest podobny do kodu synchronicznego
Atul Agrawal
@AtulAgrawal tak, o to chodzi. Async / await pozwala pisać kod asynchroniczny w stylu synchronicznym, dzięki czemu można uniknąć „piekła zwrotnego” i łączenia wielu obietnic razem. Moim zdaniem jest to bardzo przyjemny sposób na pisanie kodu asynchronicznego
Dana Woodman
więc jaka jest korzyść z używania javascript, jeśli synchronizujemy kod asynchroniczny. ponieważ większość ludzi używa javascript ze względu na jego asynchroniczny charakter
Atul Agrawal
2
@AtulAgrawal otrzymujesz to, co najlepsze z obu światów - lubisz asynchroniczną naturę języka (ponieważ powyższy kod będzie nadal wykonywany asynchronicznie - uwalniając wątek i wszystko) i zgrywasz zalety bardziej przyjaznego dla programisty stylu kodowania. Nie obyło się jednak bez problemów ...
ZenMaster
15

try-catch w javascript jest tak samo ważny i użyteczny jak w każdym innym języku, który je implementuje. Jest jeden główny powód, dla którego nie jest tak często używany w javascript, jak w innych językach. Z tego samego powodu javascript jest postrzegany jako brzydki język skryptowy, z tego samego powodu, dla którego ludzie myślą, że programiści javascript nie są prawdziwymi programistami:

  • JavaScript jest niezwykle dostępnym i wszechobecnym językiem

Sam fakt, że tak wiele osób jest narażonych na javascript (z powodu jedynego obsługiwanego języka przez przeglądarki) oznacza, że ​​masz dużo nieprofesjonalnego kodu. Oczywiście istnieje również wiele drobnych powodów:

  • niektóre rzeczy w javascript są asynchroniczne i dlatego nie są w catchstanie (rzeczy asynchroniczne)
  • wiele mówi się o tym, jak try-catch ma ogromny wpływ na wydajność. Ma trochę wyższą wydajność , ale dla większości kodów jest tego warte.
  • javascript został (niestety) zaimplementowany w sposób, który często dyskretnie ignoruje błędy (na przykład automatycznie zmienia ciągi znaków na liczby)

Niezależnie od tego, try-catch powinny być stosowane, ale oczywiście trzeba nauczyć się je właściwie wykorzystać - jak wszystko w programowaniu.

użytkownik250878
źródło
3
I dlaczego kiedykolwiek przegłosowałeś, och cichy zwycięzca?
user250878
Zgoda. Myślę, że ogólnie przyjęta odpowiedź jest prawdziwa, ale istnieją dobre powody, aby używać try-catch, a nawet rzucać, inaczej niż w przypadku obiektów rodzimych. Gdy błąd jest generowany, a nie przekazywany do wywołania zwrotnego, zatrzymuje dalsze wykonywanie i przeskakuje stos do pierwszego bloku, niż może go obsłużyć. Jest to ogromna zaleta i ma sens w przypadku operacji synchronicznych, zwłaszcza jeśli dotyczą one głębokiego zagnieżdżania. Szalenie sugeruje, że wyjątki są „złe” (no cóż, z innych oczywistych powodów) - w rzeczywistości są bardzo przydatnym narzędziem o wyjątkowej „sile”.
Semicolon
2
Czy poświęciłeś kiedyś czas na przeczytanie kodu źródłowego, powiedzmy, jQuery? Prawie nie ma tam żadnego try-catch, z wyjątkiem dokładnie tych scenariuszy wymienionych w zaakceptowanej odpowiedzi: do wykrywania funkcji i używania obiektów natywnych / hosta, które generują błędy. Wywoływanie kodu, który nie używa dużo try-catch (jak jQuery), jest „nieprofesjonalne”. Szczerze mówiąc, myślę, że szczególnie nowi programiści JavaScript pochodzący z Javy mają tendencję do nadmiernego używania funkcji językowych, takich jak try-catch.
Stijn de Witt
6

Uważam, że wiele powodów, które try..catchsą rzadkie w JavaScript, to fakt, że język ma dość wysoką tolerancję na błędy. Zdecydowaną większość sytuacji można rozwiązać za pomocą kontroli kodu, dobrych wartości domyślnych i zdarzeń asynchronicznych. W niektórych przypadkach po prostu użycie wzorca zapobiegnie problemom:

function Foo() {
    //this may or may not be called as a constructor!!
    //could accidentally overwrite properties on window
}

function Bar() {
    if (!(this instanceof Bar)) {
        return new Bar();
    }
    //this will only work on Bar objects, and wont impact window
}

Niektóre z głównych problemów w innych językach, które powodują wyjątki, po prostu nie istnieją w JS. Rzutowanie typu nie jest potrzebne przez większość czasu. Zamiast tego preferowaną metodą jest zazwyczaj sprawdzenie funkcji (wymuszenie określonego interfejsu):

function doFoo(arg) {
    if (arg.foo) {
        arg.foo();
    } else {
        Bar.prototype.foo.call(arg);
    }
}

Wraz z dodaniem async/ awaitdo języka try..catchstaje się coraz bardziej powszechne. Obiecuje, że jest formą asynchroniczną try..catch, dlatego warto oczekiwać, że:

doSomething().then(
  doSomethingWithResult,
  doSomethingWithError
)

zamiast tego należy zapisać jako:

try {
  const result = await doSomething()
  doSomethingWithResult(result)
} catch (e) {
  doSomethingWithError(e)
}
zzzzBov
źródło
3

Być może kolejnym powodem, dla którego try / catch nie jest zbyt często używany w Javascript, jest to, że konstrukcja nie była dostępna w pierwszych wersjach Javascript ... została dodana później.

W rezultacie niektóre starsze przeglądarki nie obsługują tego. (W rzeczywistości może powodować błąd analizatora składni / składni w niektórych starszych przeglądarkach, co jest trudniejsze do „zaprogramowania w obronie” przed większością innych rodzajów błędów).

Co ważniejsze, ponieważ początkowo nie był dostępny, wbudowane funkcje JavaScript, które zostały początkowo wydane (co w wielu językach można by nazwać funkcjami „bibliotecznymi”), nie korzystają z niego. (Nie działa zbyt dobrze, aby „złapać” błąd z someobject.somefunction (), jeśli nie „wyrzuca”, ale zamiast tego zwraca po prostu „null”, gdy napotka problem.)


Jeszcze innym możliwym powodem jest to, że mechanizm try / catch początkowo nie wydawał się potrzebny (i nadal nie wydaje się tak przydatny). Jest to naprawdę potrzebne tylko wtedy, gdy połączenia są rutynowo zagnieżdżane na kilku poziomach; samo zwrócenie jakiegoś ERRNO działałoby dobrze w przypadku połączeń bezpośrednich (chociaż aby było naprawdę przydatne, gdy tylko jest dostępne, najlepszą praktyką w większości języków jest używanie go wszędzie, a nie tylko w przypadku głęboko zagnieżdżonych połączeń). Ponieważ początkowo oczekiwano, że logika Javascript będzie niewielka i prosta (w końcu jest to tylko dodatek do strony internetowej :-), wywołania funkcji nie były głęboko zagnieżdżone, więc mechanizm try / catch nie wydawał się konieczny.

Chuck Kollars
źródło
3
Nie nie nie, absolutnie nie tak. Silniki, które te stare nie mają już znaczenia przez DŁUGI czas, a społeczność javascript w ogóle szybko zrzuca stare rzeczy, gdy jest to uzasadnione. Założę się nawet, że większość programistów JavaScriptu jest teraz po wersji IE6.
Camilo Martin
0

Uważam, że nie są tak często używane, ponieważ zgłaszanie wyjątków w kodzie klienta JavaScript utrudnia debugowanie strony.

Zamiast rzucać wyjątki, generalnie wolę pokazywać wszystkie alerty, (tj. alert("Error, invalid...");)

Może to zabrzmieć dziwnie, ale po stronie klienta występują błędy JavaScript, jeśli klient korzysta ze strony, którą zbudowałeś, a strona zgłasza wyjątek, chyba że klient jest specjalistą od kodowania, nie ma mowy, by kiedykolwiek mógł powiedzieć o co ci chodzi.

Zadzwoni tylko do Ciebie, mówiąc: „Hej, strona X nie działa!”, A następnie od Ciebie zależy, czy uda Ci się znaleźć to, co poszło nie tak i gdzie w kodzie.

Używając zamiast tego pola alertu, bardziej prawdopodobne jest, że zadzwoni i powie coś w stylu: „Hej, kiedy kliknę przycisk A strony X, wyświetli się pole z napisem ...” , zaufaj mi, łatwiej będzie znaleźć błąd.

Marco Demaio
źródło