#angular #rxjs #ngrx #ngrx-effects
#angular #rxjs #ngrx #ngrx-эффекты
Вопрос:
Я использую ngrx8, rxjs6 и angular 9
После входа в систему мне нужно вызвать другую службу. когда эта служба выдает ошибку, я хочу перехватить часть моего эффекта, чтобы обработать ее, проблема в том, что я перехватываю ошибку в try catch и вижу журнал, но catchError
не запускается.
Упрощенный код
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 }));
}),
);
});
Мой реальный код
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 }));
}),
);
});
Комментарии:
1.
switchMap
не ожидаетasync
функции2. @RafiHenig как это решить?
3.
@Reza
исправлен мой ответ
Ответ №1:
Вместо того, чтобы использовать Async-Await
синтаксис для ожидания завершения, Promise
возвращаемого matrixService.initClient
(что не сработало бы в текущем контексте из-за того, что switchMap
оператор не ожидает async
функций), рассмотрите возможность возврата его, не дожидаясь его, поскольку оно было бы преобразовано в Observable
(благодаря switchMap
принятию оператором Promise
), что приводит к this.userService.login
ожиданию.
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 }));
})
)
);
Комментарии:
1. Да, это работает, хотя мне нужно было отправить loginSuccess даже после того, как initClient выдает ошибку
Ответ №2:
основываясь на комментариях, я немного изменю предыдущие ответы
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 }));
})
)
);
Я меняю порядок, всегда срабатывая AuthActions.loginSuccess()
, а затем this.matrixService.initClient(userName, password)
.
catchError
не нужно вызывать дважды, любая ошибка, сгенерированная в результате вызова службы, будет перехвачена самым внешним catchError
оператором.