So werfen Sie einen Fehler vom RxJS-Kartenoperator (eckig)

90

Ich möchte einen Fehler von meiner beobachtbar ist werfen Karte Betreiber auf der Grundlage einer Bedingung. Zum Beispiel, wenn keine korrekten API-Daten empfangen werden. Bitte beachten Sie den folgenden Code:

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.") );
}

Grundsätzlich möchte ich, wie Sie im Code sehen können, einen Fehler auslösen, wenn die Antwort (res-Objekt) kein 'bearerToken' enthält. Damit geht es in meinem Abonnement in den unten genannten 2. Parameter (handleError).

.subscribe(success, handleError)

Irgendwelche Vorschläge?

Hassan
quelle
4
Was ist mit throw 'Valid token not returned';?
Günter Zöchbauer
Kann nicht kompiliert werden
Hassan
Genaue Fehlermeldung bitte.
Günter Zöchbauer
2
Oh, tut mir leid, es funktioniert nicht mit return throw 'message here', aber ohne das returnSchlüsselwort. Lassen Sie mich überprüfen, ob es logisch richtig funktioniert.
Hassan
Der Fehlertext wird in der subscribeMethode nicht empfangen und .finally()der Stream wird ebenfalls ausgelöst. (Allerdings wird die Hinrichtung gestoppt, was eine gute Sache ist)
Hassan

Antworten:

139

Wirf den Fehler einfach in den map()Operator. Alle Rückrufe in RxJS werden mit Try-Catch-Blöcken umschlossen, damit sie abgefangen und dann als errorBenachrichtigung gesendet werden .

Dies bedeutet, dass Sie nichts zurückgeben und nur den Fehler auslösen:

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

Das throwError()(früher Observable.throw()in RxJS 5) ist ein Observable, das nur eine errorBenachrichtigung sendet , sich map()aber nicht darum kümmert, was Sie zurückgeben. Selbst wenn Sie ein Observable zurückgeben map(), wird es als nextBenachrichtigung übergeben.

Als letztes müssen Sie wahrscheinlich nicht verwenden .catchError()(früher catch()in RxJS 5). Wenn Sie im Falle eines Fehlers Nebenwirkungen ausführen müssen, ist es beispielsweise besser, diese zu verwenden tap(null, err => console.log(err))(früher do()in RxJS 5).

Jan 2019: Aktualisiert für RxJS 6

Martin
quelle
1
Danke @martin - Ja, Ihre Lösung funktioniert. Ich hatte auch ein Problem mit meiner logError-Methode, auf das @ GünterZöchbauer hingewiesen hat. Ich musste returndas Fehlerobjekt daraus ziehen und jetzt funktioniert es perfekt :) Danke!
Hassan
@martin: Könnten Sie bitte herausfinden, warum wir Sie hier nicht wollen .catch ()?
Bob
1
@ Bob Weil das OP catch()nur zum Protokollieren und erneuten Auslösen des Fehlers verwendet wurde, was unnötig ist, wenn Sie nur einen Nebeneffekt ausführen möchten (Protokollieren des Fehlers) und es einfacher ist, nurdo()
Martin
1
Ist das identisch mit return throwError(new Error('Valid token not returned'));?
Simon_Weaver
@ Simon_Weaver nein ist es nicht. return throwError()gibt ein zurück Observable<never>, dies unterbricht den beobachtbaren Strom sofort, ohne überhaupt zurückzukehren.
Stellen Sie Monica
22

Wenn Sie das Gefühl haben throw new Error(), nicht beobachtbar zu sein, können Sie Folgendes verwenden switchMap:

// 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 
   }
});

oder genauer:

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

Das Verhalten wird das gleiche sein, es ist nur eine andere Syntax.

Sie sagen wörtlich "Wechsel" von der in der Pipe beobachtbaren http zu einer anderen beobachtbaren, die entweder nur den Ausgabewert "umschließt", oder eine neue beobachtbare "Fehler".

Vergiss nicht zu setzen of , sonst erhalten Sie verwirrende Fehlermeldungen.

Das Schöne an 'switchMap' ist auch, dass Sie eine ganz neue 'Befehlskette' zurückgeben können, wenn Sie möchten - für welche Logik auch immer saveJwt.

Simon_Weaver
quelle
3
Als ich anfing, an switchMapeine asynchrone ifAussage zu denken, machten die Dinge viel mehr Sinn :-)
Simon_Weaver
3

Obwohl diese Frage bereits beantwortet ist, möchte ich meinen eigenen Ansatz teilen (auch wenn er sich nur geringfügig von oben unterscheidet).

Ich würde entscheiden, was getrennt von der Zuordnung zurückgegeben wird und umgekehrt. Ich bin mir nicht sicher, welcher Operator dafür am besten geeignet ist, also werde ich ihn verwenden tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);
christo8989
quelle
Der Rückgabewert von tapwird ignoriert. Dieser Code macht etwas anderes als es sagt
sf
Ich gewöhne mich immer noch an rxjs. Wäre die Verwendung von switchMap besser? Kann jemand einen anderen Operator vorschlagen oder direkt bearbeiten?
christo8989
Ich denke, dass vorgeschlagen throw new Error()die beste Option bisher ist
sf