comment throwError dans switchMap dans l'effet ngrx

Aug 19 2020

J'utilise ngrx8, rxjs6 et angular 9

Après la connexion, je dois appeler un autre service. lorsque ce service génère une erreur, je veux catchError une partie de mon effet pour le gérer, le problème est que j'attrape l'erreur dans try catch et que je vois le journal, mais qu'il catchErrorn'est pas déclenché.

Code simplifié

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

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

sortie console

Réponses

1 RafiHenig Aug 19 2020 at 04:23

Plutôt que d'utiliser Async-Awaitla syntaxe pour attendre que le Promiseretourné par matrixService.initClientse termine (ce qui ne fonctionnerait pas dans le contexte actuel car l' switchMapopérateur n'attend pas les asyncfonctions), envisagez de le renvoyer sans l'attendre, car il serait converti en un Observable(grâce à l' switchMapopérateur accepter Promise) ayant pour résultat d' this.userService.loginêtre attendu.

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 }));
    })
  )
);
HamzaZaidi Aug 19 2020 at 22:01

sur la base des commentaires, je modifierai un peu les réponses précédentes

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

J'inverse l'ordre, toujours en tirant AuthActions.loginSuccess(), puis this.matrixService.initClient(userName, password).

catchErrorn'a pas besoin d'être appelé deux fois, toute erreur générée par l'appel de service sera capturée dans l' catchErroropérateur le plus externe.