jak throwError in switchMap w efekcie ngrx
Używam ngrx8, rxjs6 i angular 9
Po zalogowaniu muszę zadzwonić do innego serwisu. gdy ta usługa zgłasza błąd, chcę catchError część mojego efektu, aby go obsłużyć, problem polega na tym, że wychwytuję błąd w try catch i widzę dziennik, ale catchErrornie jest uruchamiany.
Uproszczony kod
login$ = createEffect(() => { return this.actions$.pipe(
ofType(AuthActions.login),
switchMap((action) =>
this.userService.login(action.userName, action.password)
.pipe(
switchMap((token) => {
return throwError(new Error('hello'));
}),
map((token) => AuthActions.loginSuccess()),
catchError((error) => {
console.error('error', error); // I don't see this in console
return of(AppError({ error }));
})),
),
catchError((error) => {
console.error('error 2', error);
return of(AppError({ error }));
}),
);
});
Mój prawdziwy kod
login$ = createEffect(() => { return this.actions$.pipe(
ofType(AuthActions.login),
switchMap((action) =>
this.userService.login(action.userName, action.password)
.pipe(
switchMap(async (token) => {
try {
await this.matrixService.initClient(action.userName, action.password);
return of(token);
}
catch (error) {
console.log('catch error', error); // I see this in console
return throwError(error);
}
}),
map((token) => AuthActions.loginSuccess()),
catchError((error) => {
console.error('error', error); // I don't see this in console
return of(AppError({ error }));
})),
),
catchError((error) => {
console.error('error 2', error);
return of(AppError({ error }));
}),
);
});
wyjście konsoli
Odpowiedzi
Zamiast używać Async-Awaitskładni do czekania na zakończenie Promisezwróconego przez matrixService.initClient(co nie działałoby w bieżącym kontekście, ponieważ switchMapoperator nie czekał na asyncfunkcje), rozważ zwrócenie go bez czekania na to, ponieważ zostałby przekonwertowany na Observable(dzięki switchMapoperatorowi akceptowanie Promise) skutkujące this.userService.loginoczekiwaniem.
login$ = createEffect(() => this.actions$
.pipe(
ofType(AuthActions.login),
switchMap(({ userName, password }) => this.userService.login(userName, password)
.pipe(
switchMap(() => this.matrixService.initClient(userName, password)),
map((token) => AuthActions.loginSuccess()),
catchError((error) => {
console.error('error', error);
return of(AppError({ error }));
})
)
),
catchError((error) => {
console.error('error 2', error);
return of(AppError({ error }));
})
)
);
na podstawie komentarzy zmodyfikuję nieco poprzednie odpowiedzi
login$ = createEffect(() => this.actions$
.pipe(
ofType(AuthActions.login),
switchMap(({ userName, password }) => this.userService.login(userName, password)
.pipe(
map((token) => AuthActions.loginSuccess())
tap(() => this.matrixService.initClient(userName, password)),
)
),
catchError((error) => {
console.error('error 2', error);
return of(AppError({ error }));
})
)
);
Zmieniam kolejność, zawsze strzelam, AuthActions.loginSuccess()a potem this.matrixService.initClient(userName, password).
catchErrornie musi być wywoływane dwa razy, każdy błąd wygenerowany przez wezwanie serwisowe zostanie przechwycony w najbardziej zewnętrznym catchErroroperatorze.