как вызвать ошибку в switchMap в эффекте ngrx

#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 оператором.