Aktualizacja:
Aby pomóc przyszłym widzom tego posta, stworzyłem to demo odpowiedzi Plumy .
Pytanie:
Mój cel wydaje się dość prosty.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
Problem polega na tym, że jeśli nie uda mi się w kroku 1, oba stepError(1)
ORAZ stepError(2)
są uruchamiane. Jeśli nie return $q.reject
wtedy stepError(2)
nie zostanie zwolniony, ale step(2)
będzie, co rozumiem. Osiągnąłem wszystko oprócz tego, co próbuję zrobić.
Jak napisać obietnice, aby móc wywołać funkcję w przypadku odrzucenia, bez wywoływania wszystkich funkcji w łańcuchu błędów? Czy jest inny sposób na osiągnięcie tego?
Oto demonstracja na żywo, więc masz coś do zrobienia.
Aktualizacja:
W pewnym sensie to rozwiązałem. Tutaj wyłapuję błąd na końcu łańcucha i przekazuję dane do, reject(data)
żeby wiedzieć, jaki problem obsłużyć w funkcji błędu. To właściwie nie spełnia moich wymagań, ponieważ nie chcę polegać na danych. Byłoby kiepskie, ale w moim przypadku czystsze byłoby przekazanie wywołania zwrotnego błędu do funkcji, zamiast polegać na zwracanych danych w celu ustalenia, co należy zrobić.
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Promise.prototype.catch()
przykłady na MDN pokazują rozwiązania dla dokładnie tych samych problemów.Odpowiedzi:
Powodem, dla którego Twój kod nie działa zgodnie z oczekiwaniami, jest to, że w rzeczywistości robi coś innego niż myślisz, że robi.
Powiedzmy, że masz coś takiego:
Aby lepiej zrozumieć, co się dzieje, udajmy, że jest to kod synchroniczny z blokami
try
/catch
:Program
onRejected
obsługi (drugi argumentthen
) jest zasadniczo mechanizmem korekcji błędów (podobnie jakcatch
blok). Jeśli zostanie zgłoszony błądhandleErrorOne
, zostanie przechwycony przez następny catch block (catch(e2)
) i tak dalej.To oczywiście nie jest to, co zamierzałeś.
Powiedzmy, że chcemy, aby cały łańcuch rozwiązywania problemów zawiódł bez względu na to, co pójdzie nie tak:
Uwaga: możemy zostawić miejsce, w
handleErrorOne
którym się znajduje, ponieważ zostanie wywołane tylko wtedy, gdystepOne
odrzuci (jest to pierwsza funkcja w łańcuchu, więc wiemy, że jeśli łańcuch zostanie odrzucony w tym momencie, może to być spowodowane tylko obietnicą tej funkcji) .Ważną zmianą jest to, że programy obsługi błędów dla innych funkcji nie są częścią głównego łańcucha obietnic. Zamiast tego każdy krok ma swój własny „łańcuch podrzędny”,
onRejected
który jest wywoływany tylko wtedy, gdy krok został odrzucony (ale nie może być osiągnięty bezpośrednio przez łańcuch główny).Powodem, dla którego to działa, jest to, że oba
onFulfilled
ionRejected
są opcjonalnymi argumentamithen
metody. Jeśli obietnica jest spełniona (tj. Rozwiązana), a następnythen
w łańcuchu nie maonFulfilled
uchwytu, łańcuch będzie kontynuowany, dopóki nie będzie takiego z takim handlerem.Oznacza to, że następujące dwie linie są równoważne:
Ale poniższy wiersz nie jest równoważny z dwoma powyższymi:
Biblioteka obietnic
$q
Angulara jest oparta naQ
bibliotece kriskowala (która ma bogatsze API, ale zawiera wszystko, co można znaleźć$q
). Dokumentacja Q API na GitHub może okazać się przydatna. Q implementuje specyfikację Promises / A + , która szczegółowo opisuje, w jaki sposóbthen
i jak działa realizacja obietnicy.EDYTOWAĆ:
Pamiętaj również, że jeśli chcesz wyrwać się z łańcucha w programie obsługi błędów, musi on zwrócić odrzuconą obietnicę lub zgłosić błąd (który zostanie automatycznie przechwycony i zawinięty w odrzuconą obietnicę). Jeśli nie zwrócisz obietnicy,
then
zawinie wartość zwrotu w obietnicy rozwiązania dla Ciebie.Oznacza to, że jeśli nic nie zwrócisz, w rzeczywistości zwracasz rozwiązaną obietnicę wartości
undefined
.źródło
if you don't return anything, you are effectively returning a resolved promise for the value undefined.
Thanks @plumastepOne().then(stepTwo, handleErrorOne)
`stepOne (). then (null, handleErrorOne) .then (stepTwo)` Czy są one naprawdę równoważne? Myślę, że w przypadku odrzucenia wstepOne
drugiej linii kodu wykona się,stepTwo
ale pierwsza tylko wykonahandleErrorOne
i zatrzyma się. A może coś mi brakuje?Trochę za późno na imprezę, ale to proste rozwiązanie zadziałało:
Pozwala to wyrwać się z łańcucha.
źródło
.then(user => { if (user) return Promise.reject('The email address already exists.') })
.then(user => { if (user) throw 'The email address already exists.' })
Potrzebujesz powtarzającego się
.then()
łańcucha ze specjalną skrzynką na początek i specjalną skrzynką na zakończenie.Umiejętność polega na tym, aby numer kroku przypadku niepowodzenia przeszedł do ostatecznej obsługi błędu.
step(1)
bezwarunkowo..then()
z następującymi wywołaniami zwrotnymi:.then()
bez procedury obsługi sukcesu i końcowego modułu obsługi błędów.Możesz napisać całość odręcznie, ale łatwiej jest zademonstrować wzorzec za pomocą nazwanych, uogólnionych funkcji:
zobacz demo
Zwróć uwagę, jak w
step()
przypadku odroczony jest odrzucany lub rozwiązany za pomocąn
, dzięki czemu ta wartość jest dostępna dla wywołań zwrotnych w następnym.then()
łańcuchu. PostepError
wywołaniu błąd jest ponownie zgłaszany, dopóki nie zostanie obsłużony przezfinalError
.źródło
Podczas odrzucania należy przekazać błąd odrzucenia, a następnie zawinąć procedury obsługi błędów kroku w funkcję, która sprawdza, czy odrzucenie powinno zostać przetworzone, czy „ponownie wrzucone” do końca łańcucha:
Co zobaczysz na konsoli:
Oto działający kod https://jsfiddle.net/8hzg5s7m/3/
Jeśli masz określoną obsługę każdego kroku, opakowanie może wyglądać następująco:
to twój łańcuch
źródło
Jeśli dobrze rozumiem, chcesz, aby pojawił się tylko błąd dotyczący kroku, który się nie udał, prawda?
To powinno być tak proste, jak zmiana przypadku niepowodzenia pierwszej obietnicy na to:
Wracając
$q.reject()
w przypadku niepowodzenia pierwszego kroku, odrzucasz tę obietnicę, co powoduje wywołanie errorCallback w drugimthen(...)
.źródło
step(2)
. Teraz po prostu spróbowałem ponownie, to się nie dzieje. Jestem zmieszany.return step(2);
powinna być wywoływana tylko wtedy, gdy zostaniestep(1)
pomyślnie rozwiązana.return $q.reject()
, łańcuch będzie działał. W tym przypadku wszystkoreturn response
schrzanił. Zobacz: jsbin.com/EpaZIsIp/6/edithttp://jsbin.com/EpaZIsIp/20/edit
Lub zautomatyzowane dla dowolnej liczby kroków:
http://jsbin.com/EpaZIsIp/21/edit
źródło
deferred.reject(n)
, otrzymuję ostrzeżenie, że obietnica została odrzucona z obiektem nonErrorSpróbuj ro, użyj tego jak libs:
https://www.npmjs.com/package/promise-chain-break
źródło
Jeśli chcesz rozwiązać ten problem za pomocą async / await:
źródło
Dołącz procedury obsługi błędów jako oddzielne elementy łańcucha bezpośrednio do wykonywania kroków:
lub używając
catch()
:Uwaga: jest to w zasadzie ten sam wzorzec, który sugeruje Pluma w swojej odpowiedzi, ale używając nazewnictwa OP.
źródło
Znaleziono
Promise.prototype.catch()
przykłady na MDN poniżej bardzo pomocne.(Przyjęta odpowiedź wspomina,
then(null, onErrorHandler)
która jest w zasadzie taka sama jakcatch(onErrorHandler)
.)źródło
Najlepszym rozwiązaniem jest refaktoryzacja łańcucha obietnic, tak aby używał ES6 await's. Następnie możesz po prostu wrócić z funkcji, aby pominąć resztę zachowania.
Uderzam głową w ten schemat od ponad roku i używając await's jest niebo.
źródło
Użyj modułu SequentialPromise
Zamiar
Dostarcz moduł, którego zadaniem jest sekwencyjne wykonywanie żądań, śledząc bieżący indeks każdej operacji w porządku porządkowym. Zdefiniuj operację we wzorcu poleceń, aby zapewnić elastyczność.
Uczestnicy
execute
metodę łączenia i śledzenia każdej operacji. SequentialPromise zwraca Promise-Chain ze wszystkich wykonanych operacji.execute
metodę podczas przekazywania porządkowej listy opcji dla każdej operacji.Konsekwencje
Użyj SequentialPromise, gdy potrzebne jest porządkowe zachowanie rozwiązania Promise. SequentialPromise będzie śledzić indeks, dla którego odrzucono obietnicę.
Realizacja
Sens
SequentialPromise
źródło
Jeśli w którymś momencie wrócisz
Promise.reject('something')
, zostaniesz wrzucony do bloku do obietnicy.Jeśli pierwsza obietnica nie zwróci żadnego wyniku, w konsoli zostanie wyświetlony tylko komunikat „Brak wyniku” .
źródło