Wciąż jestem dość nowy w obietnicach i obecnie używam Bluebird, jednak mam scenariusz, w którym nie jestem do końca pewien, jak najlepiej sobie z tym poradzić.
Na przykład mam łańcuch obietnic w aplikacji ekspresowej, takiej jak ta:
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
Więc zachowanie, którego szukam, to:
- Przechodzi do konta za pomocą identyfikatora
- Jeśli w tym momencie nastąpi odrzucenie, zbombarduj i zwróć błąd
- Jeśli nie ma błędu, przekształć dokument zwrócony do modelu
- Sprawdź hasło w dokumencie bazy danych
- Jeśli hasła nie pasują, zbombarduj i zwróć inny błąd
- Jeśli nie ma błędu, zmień hasła
- Następnie powróć sukces
- Jeśli coś poszło nie tak, zwróć 500
Więc obecnie zaczepy nie wydają się zatrzymywać łańcucha, a to ma sens, więc zastanawiam się, czy jest sposób, abym w jakiś sposób zmusił łańcuch do zatrzymania się w określonym punkcie na podstawie błędów, czy jest lepszy sposób Skonstruować to tak, aby uzyskać jakąś formę zachowania rozgałęzień, jak w przypadku if X do Y else Z
.
Każda pomoc byłaby świetna.
javascript
node.js
promise
bluebird
Grofit
źródło
źródło
Odpowiedzi:
To zachowanie jest dokładnie jak rzut synchroniczny:
To połowa
.catch
- aby móc naprawić błędy. Może być pożądane ponowne zgłoszenie, aby zasygnalizować, że stan nadal jest błędem:Jednak to samo nie zadziała w twoim przypadku, ponieważ błąd zostanie przechwycony przez późniejszą procedurę obsługi. Prawdziwym problemem jest to, że uogólnione procedury obsługi błędów „OBSŁUGUJ WSZYSTKO” są ogólnie złą praktyką i są niezmiernie źle widziane w innych językach programowania i ekosystemach. Z tego powodu Bluebird oferuje połowy typowane i predykatowe.
Dodatkową zaletą jest to, że logika biznesowa w ogóle nie musi (i nie powinna) być świadoma cyklu żądania / odpowiedzi. Zapytanie nie jest odpowiedzialne za decydowanie o stanie HTTP i błędzie klienta, a później, w miarę rozwoju aplikacji, możesz chcieć oddzielić logikę biznesową (jak zapytać o bazę danych i jak przetwarzać dane) od tego, co wysyłasz do klienta (jaki kod statusu http, jaki tekst i jaka odpowiedź).
Oto jak napisałbym twój kod.
Najpierw
.Query
wyrzuciłbymNoSuchAccountError
podklasę, zPromise.OperationalError
której Bluebird już dostarcza. Jeśli nie jesteś pewien, jak podklasować błąd, daj mi znać.Dodatkowo podklasowałbym to dla,
AuthenticationError
a następnie zrobiłbym coś takiego:Jak widać - jest bardzo przejrzysty i możesz czytać tekst jak instrukcję obsługi tego, co dzieje się w procesie. Jest również oddzielony od żądania / odpowiedzi.
Teraz nazwałbym to z obsługi trasy jako takiego:
W ten sposób cała logika jest w jednym miejscu, a decyzja, jak obsłużyć błędy klienta, jest w jednym miejscu i nie zaśmiecają się nawzajem.
źródło
.catch(someSpecificError)
procedury obsługi dla określonego błędu jest chęć wyłapania określonego typu błędu (który jest nieszkodliwy), radzenie sobie z nim i kontynuowanie następującego przepływu. Na przykład mam kod startowy zawierający sekwencję czynności do wykonania. Pierwszą rzeczą jest odczytanie pliku konfiguracyjnego z dysku, ale jeśli brakuje tego pliku konfiguracyjnego, jest to błąd OK (program ma wbudowane ustawienia domyślne), więc mogę obsłużyć ten konkretny błąd i kontynuować resztę przepływu. Może być też lepiej, aby nie wychodzić na później.instanceof
ręcznie..catch
działa jaktry-catch
instrukcja, co oznacza, że na końcu potrzebujesz tylko jednego haczyka:źródło
Nie. Tak naprawdę nie można „zakończyć” łańcucha, chyba że wyrzuci się wyjątek, który powoduje bąbelki do końca. Zobacz odpowiedź Benjamina Gruenbauma, jak to zrobić.
Wyprowadzenie jego wzorca nie polegałoby na rozróżnieniu typów błędów, ale na wykorzystaniu błędów, które mają
statusCode
ibody
pól, które mogą być wysłane z pojedynczego, ogólnego programu.catch
obsługi. W zależności od struktury aplikacji jego rozwiązanie może być jednak czystsze.Tak, możesz rozgałęziać się z obietnicami . Oznacza to jednak opuszczenie łańcucha i „powrót” do zagnieżdżania - tak jak w przypadku zagnieżdżonej instrukcji if-else lub try-catch:
źródło
Robiłem w ten sposób:
W końcu zostawiasz swój połów. I po prostu wyrzuć błąd, gdy nastąpi to w połowie twojego łańcucha.
Twoje inne funkcje prawdopodobnie wyglądałyby mniej więcej tak:
źródło
Pewnie trochę za późno na imprezę, ale możliwe jest zagnieżdżenie
.catch
jak na zdjęciu:Mozilla Developer Network - Korzystanie z obietnic
Edycja: przesłałem to, ponieważ ogólnie zapewnia żądaną funkcjonalność. Jednak w tym konkretnym przypadku tak nie jest. Ponieważ, jak już szczegółowo wyjaśnili inni,
.catch
ma naprawić błąd. Nie możesz na przykład wysłać odpowiedzi do klienta w wielu.catch
wywołaniach zwrotnych, ponieważ.catch
bez jawnościreturn
rozwiązuje toundefined
w tym przypadku, powodując kontynuację.then
wyzwalania, nawet jeśli twój łańcuch nie jest tak naprawdę rozwiązany, potencjalnie powodując.catch
wyzwalanie i wysyłanie po kolejna odpowiedź do klienta, powodująca błąd i prawdopodobnie rzucającaUnhandledPromiseRejection
się w oczy. Mam nadzieję, że to zawiłe zdanie miało dla ciebie jakiś sens.źródło
Zamiast tego
.then().catch()...
możesz zrobić.then(resolveFunc, rejectFunc)
. Ten łańcuch obietnic byłby lepszy, gdybyś załatwił sprawy po drodze. Oto jak bym to przepisał:Uwaga:
if (error != null)
jest nieco hack do interakcji z ostatniego błędu.źródło
Myślę, że powyższa odpowiedź Benjamina Gruenbauma jest najlepszym rozwiązaniem dla złożonej sekwencji logicznej, ale oto moja alternatywa dla prostszych sytuacji. Po prostu używam
errorEncountered
flagi wraz z,return Promise.reject()
aby pominąć jakiekolwiek następnethen
lubcatch
oświadczenia. Więc wyglądałoby to tak:Jeśli masz więcej niż dwie pary następnie / catch, prawdopodobnie powinieneś użyć rozwiązania Benjamina Gruenbauma. Ale to działa w przypadku prostej konfiguracji.
Zwróć uwagę, że wersja ostateczna
catch
ma tylkoreturn;
zamiastreturn Promise.reject();
, ponieważ nie ma kolejnych,then
które powinniśmy pominąć, i liczyłoby się to jako nieobsłużone odrzucenie obietnicy, czego Node nie lubi. Jak napisano powyżej, finałcatch
zwróci pokojowo rozwiązaną obietnicę.źródło