Jak zgłosić błąd z operatora mapy RxJS (kątowy)


93

Chcę zgłosić błąd z operatora mapy mojego obserwowalnego na podstawie warunku. Na przykład, jeśli nie otrzymano poprawnych danych API. Zobacz poniższy kod:

private userAuthenticate( email: string, password: string ) {
    return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
        .map( res => { 
            if ( res.bearerToken ) {
                return this.saveJwt(res.bearerToken); 
            } else {
                // THIS DOESN'T THROW ERROR --------------------
                return Observable.throw('Valid token not returned');
            }
        })
        .catch( err => Observable.throw(this.logError(err) )
        .finally( () => console.log("Authentication done.") );
}

Zasadniczo, jak widać w kodzie, jeśli odpowiedź (obiekt res) nie ma „bearerToken”, chcę wyrzucić błąd. Więc w mojej subskrypcji trafia do drugiego parametru (handleError) wspomnianego poniżej.

.subscribe(success, handleError)

Jakieś sugestie?


4
O co chodzi throw 'Valid token not returned';?
— Günter Zöchbauer

Nieudana kompilacja
— Hassan

Dokładny komunikat o błędzie.
— Günter Zöchbauer

2
Przepraszam, nie działa, return throw 'message here'ale działa bez returnsłowa kluczowego. Sprawdzę, czy działa poprawnie logicznie.
— Hassan

Tekst błędu nie jest odbierany w subscribemetodzie, a .finally()strumień w strumieniu również jest wyzwalany. (Jednak egzekucja zostaje zatrzymana, co jest dobre)
— Hassan

Odpowiedzi:


141

Po prostu wrzuć błąd do wnętrza map()operatora. Wszystkie wywołania zwrotne w RxJS są opakowane blokami try-catch, więc zostaną przechwycone, a następnie wysłane jako errorpowiadomienie.

Oznacza to, że nic nie zwracasz i po prostu wyrzucasz błąd:

map(res => { 
  if (res.bearerToken) {
    return this.saveJwt(res.bearerToken); 
  } else {
    throw new Error('Valid token not returned');
  }
})

throwError()(Dawniej Observable.throw()w RxJS 5) jest zauważalny, które po prostu wysyła errorpowiadomienie, ale map()nie obchodzi mnie to, czego wracać. Nawet jeśli zwrócisz z map()niego Observable , zostanie przekazane jako nextpowiadomienie.

Ostatnia rzecz, prawdopodobnie nie musisz jej używać .catchError()(poprzednio catch()w RxJS 5). Jeśli potrzebujesz wykonać jakieś efekty uboczne, gdy wystąpi błąd, lepiej użyć na przykład tap(null, err => console.log(err))(dawniej do()w RxJS 5).

Sty 2019: Zaktualizowano dla RxJS 6


1
Dzięki @martin - Tak, Twoje rozwiązanie działa. W rzeczywistości miałem również problem w mojej metodzie logError, na co zwrócił uwagę @ GünterZöchbauer. Miałem do returnobiektu błąd z niego i teraz działa idealnie :) Dzięki!
— Hassan

@martin: Czy mógłbyś wyjaśnić, dlaczego nie chcielibyśmy tutaj .catch ()?
— Bob

1
@Bob Ponieważ OP używał catch()tylko do logowania i ponownego zgłaszania błędu, co jest niepotrzebne, jeśli chcesz tylko wykonać efekt uboczny (rejestrowanie błędu) i jest łatwiejsze w użyciu po prostudo()
— martin

1
Czy to jest identyczne z return throwError(new Error('Valid token not returned'));?
— Simon_Weaver,

@Simon_Weaver nie, nie jest. return throwError()zwraca an Observable<never>, to po prostu natychmiast przerywa obserwowalny strumień, bez żadnego powrotu.
— Przywróć Monikę

25

Jeśli throw new Error()wydaje ci się, że wydaje się nieobserwowalne - tak jak możesz użyć throwError(...)z switchMapzamiast map(różnica polega na tym, że switchMapzwraca nową obserwowalną):

// this is the import needed for throwError()
import { throwError } from 'rxjs';


// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => { 
   if (res.bearerToken) {
      return of(this.saveJwt(res.bearerToken)); 
   } 
   else {
      return throwError('Valid token not returned');  // this is 
   }
});

lub bardziej zwięźle:

this.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

Zachowanie będzie takie samo, to po prostu inna składnia.

Dosłownie mówisz „przełącz” z http obserwowalnego w potoku na inną obserwowalną, która jest albo po prostu „zawijaniem” wartości wyjściowej, albo nowym obserwowalnym „błędem”.

Nie zapomnij wstawić of, bo otrzymasz mylące komunikaty o błędach.

Piękno „switchMap” polega również na tym, że jeśli chcesz, możesz zwrócić cały nowy „łańcuch” poleceń - bez względu na to, z jaką logiką trzeba się uporać saveJwt.


4
Kiedyś zacząłem myśleć switchMapo asynchronicznej ifwypowiedzi - rzeczy miały dużo więcej sensu :-)
— Simon_Weaver

3

Chociaż na to pytanie już udzielono odpowiedzi, chciałbym podzielić się moim podejściem (chociaż różni się ono tylko nieznacznie od powyższego).

Zdecydowałbym, co jest zwracane niezależnie od mapowania i na odwrót. Nie jestem pewien, który operator jest najlepszy do tego, więc użyję tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);

zwracana wartość tapjest ignorowana. ten kod działa inaczej niż mówi
— sf

Nadal przyzwyczajam się do rxjs. Czy użycie switchMap byłoby lepsze? Czy ktoś może zasugerować innego operatora lub bezpośrednio edytować?
— christo8989

Myślę, że ta sugerowana throw new Error()opcja jest jak dotąd najlepsza
— sf
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.