Dlaczego nie mogę po prostu wrzucić Error
wywołania zwrotnego wewnątrz catch i pozwolić procesowi obsłużyć błąd tak, jakby znajdował się w jakimkolwiek innym zakresie?
Jeśli nie zrobię, console.log(err)
nic nie zostanie wydrukowane i nic nie wiem o tym, co się stało. Proces właśnie się kończy ...
Przykład:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
Jeśli wywołania zwrotne są wykonywane w głównym wątku, dlaczego zostają Error
połknięte przez czarną dziurę?
javascript
asynchronous
promise
throw
es6-promise
demian85
źródło
źródło
.catch(…)
powraca..catch((e) => { throw new Error() })
, napisz.catch((e) => { return Promise.reject(new Error()) })
lub po prostu.catch((e) => Promise.reject(new Error()))
Odpowiedzi:
Jak wyjaśnili inni, „czarna dziura” polega na tym, że wrzucenie do środka
.catch
kontynuuje łańcuch z odrzuconą obietnicą i nie ma już żadnych zaczepów, co prowadzi do niezakończonego łańcucha, który połyka błędy (źle!)Dodaj jeszcze jeden haczyk, aby zobaczyć, co się dzieje:
do1().then(do2).catch(function(err) { //console.log(err.stack); // This is the only way to see the stack throw err; // Where does this go? }).catch(function(err) { console.log(err.stack); // It goes here! });
Zaczep w środku łańcucha jest przydatny, gdy chcesz, aby łańcuch działał pomimo nieudanego kroku, ale ponowne rzucenie jest przydatne, aby kontynuować niepowodzenie po wykonaniu takich czynności, jak rejestrowanie informacji lub kroki czyszczenia, być może nawet zmieniając który błąd Jest rzucony.
Sztuczka
Aby błąd pojawił się jako błąd w konsoli internetowej, zgodnie z pierwotnym zamysłem, używam tej sztuczki:
.catch(function(err) { setTimeout(function() { throw err; }); });
Nawet numery wierszy przetrwały, więc łącze w konsoli internetowej prowadzi mnie bezpośrednio do pliku i wiersza, w którym wystąpił (oryginalny) błąd.
Dlaczego to działa
Każdy wyjątek w funkcji nazywanej programem obsługi wypełnienia obietnicy lub odrzucenia jest automatycznie konwertowany na odrzucenie obietnicy, którą masz zwrócić. Dba o to kod obietnicy, który wywołuje twoją funkcję.
Z drugiej strony funkcja wywoływana przez setTimeout zawsze działa ze stabilnego stanu JavaScript, tj. Działa w nowym cyklu w pętli zdarzeń JavaScript. Wyjątki nie są przechwytywane przez nic i trafiają do konsoli internetowej. Ponieważ
err
zawiera wszystkie informacje o błędzie, w tym oryginalny stos, numer pliku i linii, nadal jest zgłaszany poprawnie.źródło
function logErrors(e){console.error(e)}
następnie użyj go jakdo1().then(do2).catch(logErrors)
. Przy okazji odpowiedź sama w sobie jest świetna, +1window.onerror
obsługi zdarzeń. Tylko wykonującsetTimeout
sztuczkę, można to zrobić. W przeciwnym raziewindow.onerror
nigdy nie usłyszysz o błędach, które wystąpiły w Promise.console.log
lubpostErrorToServer
, możesz po prostu zrobić to, co trzeba. Nie ma powodu, dla którego kodwindow.onerror
nie może być rozłożony na oddzielną funkcję i wywołany z dwóch miejsc. Prawdopodobnie jest jeszcze krótsza niżsetTimeout
linia.Ważne rzeczy do zrozumienia
Obie funkcje
then
icatch
zwracają nowe obiekty obietnicy.Rzucenie lub jawne odrzucenie przesunie aktualną obietnicę do stanu odrzucenia.
Ponieważ
then
icatch
zwracają nowe obiecane obiekty, można je łączyć.Jeśli wrzucisz lub odrzucisz wewnątrz programu obsługi obietnic (
then
lubcatch
), zostanie to rozpatrzone w następnym programie obsługi odrzucenia na ścieżce łączenia.Jak wspomniano w jfriend00, programy obsługi
then
icatch
nie są wykonywane synchronicznie. Kiedy przewodnik rzuca, natychmiast się kończy. Zatem stos zostanie rozwinięty, a wyjątek zostanie utracony. Dlatego rzucenie wyjątku odrzuca obecną obietnicę.W twoim przypadku odrzucasz do środka
do1
, rzucając jakiśError
przedmiot. Teraz aktualna obietnica będzie w stanie odrzucenia, a kontrola zostanie przekazana kolejnemu handlerowi, co jestthen
w naszym przypadku.Ponieważ program
then
obsługi nie ma modułu obsługi odrzucania,do2
nie zostanie on w ogóle wykonany. Możesz to potwierdzić, używającconsole.log
wewnątrz. Ponieważ bieżąca obietnica nie ma procedury obsługi odrzucenia, zostanie również odrzucona z wartością odrzucenia z poprzedniej obietnicy, a kontrola zostanie przekazana kolejnej obsłudze, którą jestcatch
.Podobnie jak w
catch
przypadku obsługi odrzucania, kiedy to zrobiszconsole.log(err.stack);
, możesz zobaczyć ślad stosu błędów. Teraz wyrzucaszError
z niego obiekt, więc obietnica zwrócona przezcatch
będzie również w stanie odrzuconym.Ponieważ nie dołączyłeś żadnej procedury obsługi odrzucania do programu
catch
, nie możesz obserwować odrzucenia.Możesz podzielić łańcuch i lepiej to zrozumieć, w ten sposób
var promise = do1().then(do2); var promise1 = promise.catch(function (err) { console.log("Promise", promise); throw err; }); promise1.catch(function (err) { console.log("Promise1", promise1); });
Wynik, który otrzymasz, będzie podobny do
Wewnątrz
catch
procedury obsługi 1 otrzymujesz wartośćpromise
obiektu jako odrzuconą.W ten sam sposób obietnica zwrócona przez
catch
handlera 1 jest również odrzucana z tym samym błędem, z jakimpromise
została odrzucona i obserwujemy to w drugimcatch
handlerze.źródło
.then()
procedury obsługi są asynchroniczne (stos jest rozwijany przed wykonaniem), więc wyjątki w nich muszą zostać zamienione na odrzucenia, w przeciwnym razie nie byłoby programów obsługi wyjątków, które mogłyby je złapać.Wypróbowałem
setTimeout()
metodę opisaną powyżej ....catch(function(err) { setTimeout(function() { throw err; }); });
Irytujące okazało się, że jest to całkowicie nie do przetestowania. Ponieważ zgłasza asynchroniczny błąd, nie można go opakować w
try/catch
instrukcję, ponieważ funkcjacatch
will przestała nasłuchiwać do momentu zgłoszenia błędu .Wróciłem do słuchania, które działało doskonale, a ponieważ tak powinno być używane JavaScript, było wysoce testowalne.
return new Promise((resolve, reject) => { reject("err"); }).catch(err => { this.emit("uncaughtException", err); /* Throw so the promise is still rejected for testing */ throw err; });
źródło
Zgodnie ze specyfikacją (patrz 3.III.d) :
Oznacza to, że jeśli wyrzucisz wyjątek w
then
funkcji, zostanie on przechwycony, a Twoja obietnica zostanie odrzucona.catch
nie ma tu sensu, to tylko skrót do.then(null, function() {})
Chyba chcesz rejestrować nieobsłużone odrzucenia w swoim kodzie. Większość bibliotek obiecuje
unhandledRejection
za to. Oto istotna treść dyskusji na ten temat.źródło
unhandledRejection
haczyk jest przeznaczony dla JavaScript po stronie serwera, po stronie klienta różne przeglądarki mają różne rozwiązania. Jeszcze tego nie znormalizowaliśmy, ale to powoli, ale pewnie.Wiem, że to trochę za późno, ale trafiłem na ten wątek i żadne z rozwiązań nie było dla mnie łatwe do wdrożenia, więc wymyśliłem własne:
Dodałem małą funkcję pomocniczą, która zwraca obietnicę, na przykład:
function throw_promise_error (error) { return new Promise(function (resolve, reject){ reject(error) }) }
Następnie, jeśli mam określone miejsce w którymkolwiek z moich obietnic, w którym chcę rzucić błąd (i odrzucić obietnicę), po prostu wracam z powyższej funkcji z moim skonstruowanym błędem, na przykład:
}).then(function (input) { if (input === null) { let err = {code: 400, reason: 'input provided is null'} return throw_promise_error(err) } else { return noterrorpromise... } }).then(...).catch(function (error) { res.status(error.code).send(error.reason); })
W ten sposób kontroluję wyrzucanie dodatkowych błędów z łańcucha obietnic. Jeśli chcesz również poradzić sobie z „normalnymi” błędami obietnic, możesz rozszerzyć swój chwyt, aby osobno traktować błędy „zgłaszane samodzielnie”.
Mam nadzieję, że to pomoże, to moja pierwsza odpowiedź!
źródło
Promise.reject(error)
zamiastnew Promise(function (resolve, reject){ reject(error) })
(co i tak wymagałoby oświadczenia zwrotnego)Tak, obiecuje połknąć błędy, a można je tylko złapać
.catch
, jak wyjaśniono bardziej szczegółowo w innych odpowiedziach. Jeśli jesteś w Node.js i chcesz odtworzyć normalnethrow
zachowanie, wydrukować ślad stosu do konsoli i zakończyć proces, możesz to zrobić... throw new Error('My error message'); }) .catch(function (err) { console.error(err.stack); process.exit(0); });
źródło
unhandledRejection
wydarzeniu