ngrx 影响错误处理

ngrx effects error handling

我有一个关于@ngrx 效果的非常基本的问题:如何忽略效果执行期间发生的错误,使其不影响未来的效果执行?

我的情况如下:我有一个动作(LOGIN)和一个监听那个动作的效果。如果此效果内部发生错误,我想忽略它。发生此错误后第二次调度 LOGIN 时,应执行第二次效果。

我第一次尝试这样做是:

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

如预期的那样,第一次调度 LOGIN 会抛出并捕获错误。但是,如果我之后第二次发送 LOGIN,什么也不会发生;效果不执行。

因此我尝试了以下方法:

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

,但这会导致死循环...你知道如何在不阻止 effect 执行的情况下捕获错误吗?

ngrx 基础设施通过使用 EffectsModule.run.

导入到应用 NgModule 的提供商订阅效果

当 observable 错误和 catch returns 一个空的 obervable 时,组合的 observable 完成并且它的订阅者被取消订阅 - 这是 Observable Contract 的一部分。这就是您在效果中看不到 LOGIN 操作没有进一步处理的原因。效果在完成时取消订阅,ngrx 基础设施不会 re-subscribe。

通常,您会在 flatMap(现在称为 mergeMap)中进行错误处理:

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

组成内部可观察对象的 catch 将在效果中看到一个空的可观察对象 flattened/merged,因此不会发出任何动作。

这就是我如何处理在未找到任何数据的 Observables 上通知或不通知的选项:

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

当发生错误时,@Effect 流正在完成,阻止任何进一步的操作。

解决方案是切换到一次性流。如果在一次性流中发生错误也没关系,因为主 @Effect 流始终保持活动状态,并且未来的操作会继续执行。

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

    });

此博客中有关此技术的更多信息 post:The Quest for Meatballs: Continue RxJS Streams When Errors Occur