Search code examples
angularrxjsngrxngrx-effects

how to throwError in switchMap in the ngrx effect


I am using ngrx8, rxjs6 and angular 9

After login I need to call another service. when that service is throwing error I want to catchError part of my effect to handle it, the issue is I catch the error in try catch and I see the log, but catchError is not triggered.

Simplified code

    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 }));
            }),
        );
    });

My real code

    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 }));
            }),
        );
    });

console output enter image description here


Solution

  • Rather than using Async-Await syntax to wait for the Promise returned by matrixService.initClient to complete (which wouldn't work in the current context due to switchMap operator not waiting for async functions), consider returning it without waiting for it, as it would be converted into an Observable (thanks to switchMap operator accepting Promise) resulting in this.userService.login being awaited.

    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 }));
        })
      )
    );