Обработка ошибок ngrx

У меня есть очень простой вопрос относительно эффектов @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 в первый раз выбрасывает и ловит ошибку, как и ожидалось. Однако, если я снова отправлю логин после этого, ничего не произойдет; эффект не выполняется.

Поэтому я попробовал следующее:

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

но это приводит к бесконечному циклу... Вы знаете, как поймать ошибку, не предотвращая последующее выполнение эффекта?

Ответ 1

Инфраструктура ngrx соглашается с эффектом через поставщика, импортированного в приложение NgModule, используя EffectsModule.run.

Когда наблюдаемые ошибки и catch возвращают пустой объект, завершенные наблюдаемые завершаются, а его подписчики отписаны - эта часть Наблюдаемый контракт. И это причина, по которой вы не видите никакой обработки действий LOGIN в вашем действии. Эффект отменяется после завершения, а инфраструктура ngrx не переписывается.

Как правило, у вас будет обработка ошибок внутри 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, составленный во внутреннее наблюдаемое, увидит, что пустые наблюдаемые сплюснутые/слитые в эффект, поэтому никакое действие не будет излучаться.

Ответ 2

Поток @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();
            });

    });

Дополнительная информация об этой технике в этом сообщении в блоге: Задание для фрикадельки: продолжение потоков RxJS при возникновении ошибок

Ответ 3

вот как я обрабатываю опцию, чтобы ее уведомляли или не уведомляли в 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())));
    }