Cómo lanzar un error del operador de mapa RxJS (angular)


93

Quiero lanzar un error del operador de mapa de mi observable basado en una condición. Por ejemplo, si no se reciben los datos de API correctos. Consulte el siguiente código:

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

Básicamente, como puede ver en el código, si la respuesta (objeto res) no tiene 'bearerToken', quiero arrojar un error. De modo que en mi suscripción entra en el segundo parámetro (handleError) mencionado a continuación.

.subscribe(success, handleError)

¿Alguna sugerencia?


4
¿Qué hay de throw 'Valid token not returned';?
Günter Zöchbauer

No se puede compilar
Hassan

Mensaje de error exacto por favor.
Günter Zöchbauer

2
Lo siento, no funciona con return throw 'message here'la returnpalabra clave, pero funciona sin ella . Déjame comprobar si funciona correctamente de forma lógica.
Hassan

El texto de error no se recibe en el subscribemétodo y .finally()también se activa en la secuencia. (Sin embargo, la ejecución se detiene, lo cual es bueno)
Hassan

Respuestas:


141

Simplemente arroje el error dentro del map()operador. Todas las devoluciones de llamada en RxJS están envueltas con bloques try-catch para que se detecten y luego se envíen como una errornotificación.

Esto significa que no devuelve nada y simplemente arroja el error:

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

El throwError()(primero Observable.throw()en RxJS 5) es un Observable que solo envía una errornotificación pero map()no le importa lo que devuelva. Incluso si devuelve un Observable map(), se pasará como nextnotificación.

Por último, probablemente no necesite usar .catchError()(anteriormente catch()en RxJS 5). Si necesita realizar algún efecto secundario cuando ocurre un error, es mejor usar tap(null, err => console.log(err))(antes do()en RxJS 5) por ejemplo.

Enero de 2019: actualizado para RxJS 6


1
Gracias @martin - Sí, su solución funciona. De hecho, también tuve un problema dentro de mi método logError que @ GünterZöchbauer señaló. Tuve returnel objeto de error y ahora funciona perfectamente :) ¡Gracias!
Hassan

@martin: ¿Podrías explicar por qué no queremos que tengas .catch () aquí?
Bob

1
@Bob Porque el OP se estaba usando catch()solo para registrar y volver a lanzar el error, lo cual es innecesario si solo desea realizar un efecto secundario (registrar el error) y es más fácil de usardo()
martin

1
¿Es esto idéntico a return throwError(new Error('Valid token not returned'));?
Simon_Weaver

@Simon_Weaver no, no lo es. return throwError()devuelve un Observable<never>, esto simplemente interrumpe la secuencia observable inmediatamente, sin regresar en absoluto.
Reincorporación a Monica el

25

Si siente que throw new Error()parece no observable, puede usar throwError(...)con en switchMaplugar de map(la diferencia switchMapdevuelve un nuevo observable):

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

o más concisamente:

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

El comportamiento será el mismo, solo es una sintaxis diferente.

Literalmente está diciendo 'cambiar' del observable http en la tubería a un observable diferente, que es simplemente 'envolviendo' el valor de salida, o un nuevo 'error' observable.

No olvide poner ofo obtendrá algunos mensajes de error confusos.

Además, la belleza de 'switchMap' es que puede devolver una 'cadena' completamente nueva de comandos si lo desea, para cualquier lógica que necesite saveJwt.


4
Una vez que comencé a pensar switchMapen una ifdeclaración asincrónica , las cosas
tenían

3

Aunque esta pregunta ya está respondida, me gustaría compartir mi propio enfoque (aunque es solo un poco diferente al anterior).

Yo decidiría qué se devuelve por separado del mapeo y viceversa. No estoy seguro de qué operador es mejor para esto, así que lo usaré tap.

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

el valor de retorno de tapse ignora. este código hace algo diferente de lo que dice
sf

Todavía me estoy acostumbrando a los rxjs. ¿Sería mejor usar switchMap? ¿Alguien puede sugerir un operador diferente o editar directamente?
christo8989

Creo que lo sugerido throw new Error()es la mejor opción hasta ahora
sf
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.