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?

Hassan
fuente
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

martín
fuente
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.

Simon_Weaver
fuente
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)),
);
christo8989
fuente
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