web-dev-qa-db-fra.com

gestion des erreurs des effets ngrx

J'ai une question très basique concernant les effets @ngrx: comment ignorer une erreur qui survient pendant l'exécution d'un effet de telle sorte qu'elle n'affecte pas l'exécution ultérieure de l'effet?

Ma situation est la suivante: j'ai une action (LOGIN) et un effet qui écoute cette action. Si une erreur survient à l'intérieur de cet effet, je veux l'ignorer. Lorsque LOGIN est envoyé une seconde fois après cette erreur, l'effet doit être exécuté une seconde fois.

Ma première tentative a été:

  @Effect()
  login$ = this.actions$
    .ofType('LOGIN')
    .flatMap(async () => {
      console.debug('LOGIN');
      // throw an error
      let x = [];x[0]();
    })
    .catch(err => {
      console.error('Error at login', err);
      return Observable.empty();
    });

Envoi de LOGIN pour la première fois, lève et intercepte l'erreur, comme prévu. Cependant, si je répète LOGIN une deuxième fois par la suite, rien ne se passe; l'effet n'est pas exécuté.

J'ai donc essayé ce qui suit:

    .catch(err => {
      return this.login$;
    });

, mais cela aboutit à une boucle sans fin ... Savez-vous comment capter l'erreur sans empêcher plus tard l'exécution de l'effet?

21
highwaychile

L'infrastructure ngrx souscrit à l'effet via le fournisseur importé dans la variable NgModule de l'application à l'aide de EffectsModule.run.

Lorsque les erreurs observables et catch renvoie un observable vide, l'observable composé est terminé et ses abonnés ne sont pas abonnés - cela fait partie du Observable Contract . Et c’est la raison pour laquelle vous ne voyez plus aucun traitement des actions LOGIN dans votre effet. L'effet est désabonné à la fin et l'infrastructure ngrx ne se réabonne pas.

En règle générale, la gestion des erreurs dans la flatMap (maintenant appelée mergeMap) est la suivante:

import { Actions, Effect, toPayload } from "@ngrx/effects";

@Effect()
login$ = this.actions$
  .ofType('LOGIN')
  .map(toPayload)
  .flatMap(payload => Observable
    .from(Promise.reject('Boom!'))
    .catch(error => {
      console.error('Error at login', error);
      return Observable.empty();
    })
  });

Le catch composé dans l'observable interne verra un observable vide aplati/fusionné dans l'effet, ainsi aucune action ne sera émise.

18
cartant

Le flux @Effect est complétant lorsque l'erreur se produit, empêchant toute action ultérieure.

La solution consiste à passer à un flux jetable. Si une erreur survient dans le flux disponible, c'est correct, car le flux principal @Effect reste toujours actif et les actions futures continuent de s'exécuter.

@Effect()
login$ = this.actions$
    .ofType('LOGIN')
    .switchMap(action => {

        // This is the disposable stream!
        // Errors can safely occur in here without killing the original stream

        return Rx.Observable.of(action)
            .map(action => {
                // Code here that throws an error
            })
            .catch(error => {
                // You could also return an 'Error' action here instead
                return Observable.empty();
            });

    });

Plus d'informations sur cette technique dans cet article de blog: La quête de boulettes géantes: continuez les flux RxJS lorsque des erreurs se produisent

4
iamturns

voici comment je gère l'option d'être notifié ou non sur Observables qui n'a pas trouvé de données:

 public listenCampaignValueChanged(emitOnEmpty: boolean = false): Observable<CampaignsModelExt> {
        var campaignIdSelected$ = this.ngrxStore.select(store => store.appDb.uiState.campaign.campaignSelected)
        var campaigns$ = this.ngrxStore.select(store => store.msDatabase.sdk.table_campaigns);
        return campaignIdSelected$
            .combineLatest(campaigns$, (campaignId: number, campaigns: List<CampaignsModelExt>) => {
                return campaigns.find((i_campaign: CampaignsModelExt) => {
                    return i_campaign.getCampaignId() == campaignId;
                });
            }).flatMap(v => (v ? Observable.of(v) : ( emitOnEmpty ? Observable.of(v) : Observable.empty())));
    }
1
born2net