throw w bloku catch

0

Cześć,
próbuje któryś tydzień ;/ zrozumieć fragment książki

Teraz jednak przeglądana będzie cała tablica, nawet jeśli wartość zostanie znaleziona natychmiast na pierwszej pozycji. Co za marnotrawstwo. W pętli for można zakończyć iterację za pomocą instrukcji break, ale w pętli forEach tak się nie da, ponieważ treść pętli jest funkcją, a instrukcja break nie służy do wychodzenia z funkcji. Jednym z możliwych rozwiązań jest dostosowanie forEach, aby rozpoznawała określony typ wyjątków jako sygnał do przerwania.

var Break = {toString: function() {return "Break";}};

function forEach(array, action) {
  try {
    for (var i = 0; i < array.length; i++)
      action(array[i]);
  }
  catch (exception) {
    if (exception != Break)
      throw exception;
  }
}

Teraz gdy funkcja action zgłosi wyjątek Break, funkcja forEach go przechwyci i przerwie iterację. Obiekt przechowywany w zmiennej Break jest używany wyłącznie w celach porównawczych. Własność toString dodałem mu po to, aby ułatwić zorientowanie się, co za dziwną wartość otrzymaliśmy, gdy jakimś cudem wyjątek Break pojawi się poza forEach.

Proszę o pomoc w zrozumieniu poniższych:

Teraz gdy funkcja action zgłosi wyjątek Break

czy tu chodzi o taką normalną instrukcję "break;" - czy specjalny obiekt o "mylącej" ;/ nazwie.


drugi fragment:

var FoundSeven = {};

function hasSevenTruths(object) {
  var counted = 0;

  function count(object) {
    for (var name in object) {
      if (object[name] === true) {
        counted++;
        if (counted == 7)
          throw FoundSeven;
      }
      else if (typeof object[name] == "object") {
        count(object[name]);
      }
    }
  }

  try {
    count(object);
    return false;
  }
  catch (exception) {
    if (exception != FoundSeven)
      throw exception;
    return true;
  }
}

Wewnętrzna funkcja count jest rekurencyjnie wywoływana dla każdego obiektu będącego częścią argumentu. Gdy wartość zmiennej counted dojdzie do siedmiu, nie ma sensu kontynuować liczenia, ale sam zwrot z bieżącego wywołania funkcji count niekoniecznie zatrzyma liczenie, ponieważ pod nim mogą być jeszcze inne wywołania. Dlatego użyliśmy instrukcji throw, która powoduje wyjście z wywołań funkcji count i przejście do bloku catch.

Jednak zwrócenie jedynie wartości true w przypadku wyjątku jest niepoprawne. Coś innego mogłoby pójść nie tak i dlatego najpierw sprawdzamy, czy wyjątek jest utworzonym specjalnie na tę okazję obiektem FoundSeven. Jeśli nie, ten blok catch nie wie, jak go obsłużyć, a więc ponawia jego zgłoszenie.

Nie rozumiem tego framgentu o ponownym zgłoszeniu - throw w catchu nie wywołuje ponownie bloku catch - tylko wychodzi z niego - czy się myle :( ?

dodanie znaczników <code class="javascript"> - @furious programming

0

Odnośnie pierwszego fragmentu:
na początku kodu tworzona jest zmienna o nazwie Break (przypisany jest do niej obiekt). Za pomocą instrukcji throw możesz rzucić dowolną zmienną.
Kod, który wykorzystałby przedstawioną funkcję forEach(), wyglądałby mniej więcej tak:

        var someArray = ['pierwszy element', 'drugi element', 'trzeci element'];
                
        forEach(someObject, function(arrayItem){
                if (arrayItem == 'drugi element') {
                        throw Break;     // rzuca utworzony wcześniej obiekt Break
                }
                               
                console.log(arrayItem);
        });

Podsumowując, chodzi o specjalny obiekt o "mylącej" nazwie.

Odnośnie drugiego tematu:
w tym fragmencie kodu:

        try {
                count(object);
                return false;
        } catch (exception) {
                if (exception != FoundSeven) {
                        throw exception;
                }
                return true;
        }

W bloku catch zostanie obsłużony tylko wyjątek FoundSeven, każdy inny zostanie rzucony ponownie. Dzięki temu, jeśli wystąpi z jakichś przyczyn inny błąd (wyjątek), to zostanie on "przekazany wyżej" (czyli rzucony ponownie).
W innych językach w bloku catch można złapać konkretny typ wyjątku, wtedy cały blok reaguje tylko na ten jeden konkretny typ, niestety w JS się tak nie da i trzeba sobie radzić za pomocą if-ów.

[Adam]

0
kchteam napisał(a):

Dzięki temu, jeśli wystąpi z jakichś przyczyn inny błąd (wyjątek), to zostanie on "przekazany wyżej" (czyli rzucony ponownie).
[Adam]

czyli użycie "throw" - powoduje wyjście z bieżącej operacji i przejście do poziomu wywołania (w kazdym razie "wyżej") ? czy tak ?

a jeśli nie było by tego "throw exception" to co by się stało ? byłoby ryzyko zatrzymania programu?

1

Instrukcja throw przerywa wykonanie skryptu i przekazuje swój argument "wyżej" do najbliższego bloku catch. Generalnie try ... catch służy do obsługi błędów - za pomocą try rzuca się zazwyczaj obiekt zawierający informację o błędzie, a w bloku catch można zareagować na błąd - np. wyświetlić stosowny komunikat.
Podstawowa składnia:

try {
// jakiś kod, z którego może zostać rzucony wyjątek.
} catch (e) {
// ... reakcja na błąd
}

W przykładach z książki w bloku catch zostanie złapany każdy wyjątek jaki zostanie rzucony w bloku try, a chodzi o to, żeby złapać tylko ten konkretny, czyli Break lub FoundSeven - a to dlatego, że w tym miejscu w kodzie wiemy, jak zareagować tylko na te wyjątki, a nie na inne; mówiąc dosłownie: funkcja forEach nie wie, gdzie została wywołana i potrafi poprawnie reagować tylko na wyjątek Break. Gdyby nie było throw exception, to wszystkie inne wyjątki "ugrzęzłyby" w tym miejscu i zachodziłoby ryzyko błędnego działania całego programu. Czy program by się zatrzymał? To zależy od programu.

Więcej o try ... catch możesz poczytać tutaj: http://jstricks.com/javascript-try-catch-tutorial/

[Adam]

0

Bardzo Ci dziękuje za wyrozumiałość - rozumiałem ogólną koncepcję, ale zabrakło tego czegoś :)

0

jeszcze mam 2 małe pytania:
czy

throw exception //w odniesieniu do mojego przykładu

to to samo co

throw "jakis string"

i drugie pytanie

czy dobrze rozumuje
ze taki throw w catchu - mógłbym "złapać" w kolejnym try/catch bloku ?

0

W odniesieniu do Twojego przykładu throw exception to ciut co innego, niż throw 'jakiś string'. W Twoim przykładzie exception to wyjątek złapany wcześniej, który rzucamy ponownie, bo nie chcemy go obsługiwać w tym miejscu, natomiast throw 'jakiś string' zgubi informację o tym, jaki to był wyjątek - będzie to nowy wyjątek.

W ogólnym przypadku do throw można podać zmienną dowolnego typu - najczęściej jednak stosuje się obiekty z tego względu, że można w nich przekazać więcej informacji, niż tylko komunikat - można dołączyć np. kod błędu.

throw {message: 'Komunikat o błędzie', code: '0001'};

Odnosząc się jeszcze do ponownego rzucania wyjątku - masz rację, właśnie o to chodzi, że ten throw exception pozwoli Ci na złapanie exception w innym bloku catch - gdzieś wyżej.

try {
  var someArray = ['pierwszy element', 'drugi element', 'trzeci element'];
                
  forEach(someObject, function(arrayItem){
    if (arrayItem == 'drugi element') {
      throw Break;               // zostanie złapany w funkcji forEach
    }
                
    if (arrayItem == 'trzeci element') {
      throw {message: 'Element trzeci się nie nadaje'}; // zostanie złapany w funkcji forEach i rzucony dalej
    }

    console.log(arrayItem);
  });
} catch (e) {
  // Łapiemy wyjątek, który może "wylecieć" przy elemencie trzecim (rzucony dalej przez funkcję forEach)
  alert(e.message); // Alert tylko poglądowo - raczej staramy się nie używać :)
}

[Adam]

1 użytkowników online, w tym zalogowanych: 0, gości: 1