description |
---|
Captura errores en el Observable que se manejan devolviendo un Observable nuevo o lanzando un error |
Signatura
catchError<T, O extends ObservableInput<any>>(selector: (err: any, caught: Observable<T>) => O): OperatorFunction<T, T | ObservedValueOf<O>>
OperatorFunction<T, T | ObservedValueOf<O>>
: Un Observable que se puede originar en el Observable fuente o en el Observable retornado por la función selector
.
catchError
captura errores en el Observable fuente, manejándolos de dos maneras posibles: bien devolviendo un Observable nuevo o bien lanzando un nuevo error.
Capturar un error, retornando un Observable
import { throwError, of } from "rxjs";
import { catchError } from "rxjs/operators";
const error$ = throwError("¡Oh no!");
error$
.pipe(catchError((error) => of(`Error capturado grácilmente: ${error}`)))
.subscribe(console.log);
// Salida: Error capturado grácilmente: ¡Oh no!
Capturar un error y lanzar otro error
import { throwError, of } from "rxjs";
import { catchError } from "rxjs/operators";
const error$ = throwError("Oh no!");
error$
.pipe(
catchError((error) => {
throw `Lanzando un nuevo error: ${error}`;
})
)
.subscribe(console.log, console.error);
// Salida: (Error) Lanzando un nuevo error: Oh no!
Capturar los errores de un Observable interno
Al capturar los errores que ocurren en un Observable interno (un Observable emitido por un Observable de orden superior), se debe tener cuidado a la hora de utilizar el operador catchError
ya que, si se coloca en el sitio equivocado, el flujo del Observable fuente no seguirá ejecutándose tras capturar el error.
A continuación, se puede ver cómo el uso incorrecto de catchError
hará que, después de capturar el error que devuelve la primera petición, el flujo se completará y no se harán las otras dos peticiones restantes:
import { map, concatMap, catchError } from "rxjs/operators";
import { ajax } from "rxjs/ajax";
import { of } from "rxjs";
const pokemonId$ = of(-3, 5, 6);
function getPokemonName(id: number) {
return ajax
.getJSON(`https://pokeapi.co/api/v2/pokemon/${id}`)
.pipe(map(({ name }) => name));
}
pokemonId$
.pipe(
concatMap((id) => getPokemonName(id)),
catchError((error) => of(`¡Oh no, ha ocurrido un error! ${error}`))
)
.subscribe(console.log, console.error, () => console.log("Completado"));
// Salida: ¡Oh no, ha ocurrido un error! AjaxError: ajax error 404, Completado
Sin embargo, si se utiliza catchError
en el Observable interno, el comportamiento es el que se busca: cuando falle la primera petición, se capturará el error y el flujo seguirá ejecutándose, realizando las dos peticiones restantes:
import { map, concatMap, catchError } from "rxjs/operators";
import { ajax } from "rxjs/ajax";
import { of } from "rxjs";
const pokemonId$ = of(-3, 5, 6);
function getPokemonName(id: number) {
return ajax
.getJSON(`https://pokeapi.co/api/v2/pokemon/${id}`)
.pipe(map(({ name }) => name));
}
pokemonId$
.pipe(
concatMap((id) =>
getPokemonName(id).pipe(catchError((error) => of(`¡Oh no! ${error}`)))
)
)
.subscribe(console.log, console.error, () => console.log("Completado"));
// Salida: ¡Oh no, ha ocurrido un error!, charmeleon, charizard, Completado
Continuar con un Observable diferente cuando ocurre un error
import { of } from "rxjs";
import { map, catchError } from "rxjs/operators";
of(1, 2, 3, 4, 5)
.pipe(
map((n) => {
if (n === 4) {
throw "four!";
}
return n;
}),
catchError((err) => of("I", "II", "III", "IV", "V"))
)
.subscribe((x) => console.log(x));
// 1, 2, 3, I, II, III, IV, V
Reiniciar el Observable fuente en caso de error, parecido al operador retry()
import { of } from "rxjs";
import { map, catchError, take } from "rxjs/operators";
of(1, 2, 3, 4, 5)
.pipe(
map((n) => {
if (n === 4) {
throw "four!";
}
return n;
}),
catchError((err, caught) => caught),
take(30)
)
.subscribe((x) => console.log(x));
// 1, 2, 3, 1, 2, 3...
Lanzar un error nuevo cuando el Observable fuente lance un error
import { of } from "rxjs";
import { map, catchError } from "rxjs/operators";
of(1, 2, 3, 4, 5)
.pipe(
map((n) => {
if (n === 4) {
throw "four!";
}
return n;
}),
catchError((err) => {
throw "error en la fuente. Detalles: " + err;
})
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err)
);
// 1, 2, 3, error en la fuente. Detalles: four!