Должны ли мы использовать NEVER в некоторых случаях эффектов NgRx?

#angular #redux #rxjs #frontend #ngrx

#angular #redux #rxjs #интерфейс #ngrx

Вопрос:

В некоторых случаях использования эффектов NgRx мне интересно, как управлять «условие не выполнено» в наблюдаемом потоке. В приведенном ниже коде действия все равно должны быть отправлены, если условие выполнено, но, если условие не выполнено, это, однако, не ошибка.

   someEffect$ = createEffect(() =>this.actions$.pipe(
    ofType(SomeActions.fooFired),
    switchMap(action => this.sharedService.apiCall().pipe(
      concatMap((data: SomeType) => {
        if (data.condition === 'ok') {
          return [
            SomeActions.barFired({payload: data.payload}),
            SomeActions.successBar()
          ];
        } else {
          // EMPTY, NEVER... neither ?
        }
      }),
    )))
  );
  
  • Должен ли я создать два отдельных эффекта? (это не очень соответствует SRP)
  • Должен ли я вернуться EMTPY (но это завершает поток …) или NEVER ?
  • Мой код плохо разработан и я должен выбрать совершенно другой подход?

Ответ №1:

Это очень самоуверенный вопрос и ответ, но я скажу свои 2 слова, потому что они могут помочь.

Прежде всего, я не понимаю, почему concatMap это полезно для. Обычно вложенных observables можно избежать для более чистого результата.

Во-вторых, когда вы создаете Action для Success , вы Fail тоже должны обрабатывать. Что-то вроде этого:

   someEffect$ = createEffect(() =>this.actions$.pipe(
  ofType(SomeActions.fooFired),
  switchMap(action => this.sharedService.apiCall()),
  switchMap((data: SomeType) => {
        if (data.condition === 'ok') {
          return SomeActions.successBar();
        } else {
          return SomeActions.failBar();
        }
      }),
    )))
  );
  

Если вы хотите выполнить другое действие для достижения успеха, я бы создал другую причину эффекта, как вы уже сказали: (это не очень соответствует SRP)

Должен ли я возвращать EMTPY (но это завершает поток …) или НИКОГДА?

Постарайтесь, чтобы это было просто. Соглашения иногда могут быть полезны, но они не помогают другим понять, что вы хотите сказать.

Постарайтесь, чтобы ваш код был максимально чистым и содержательным, и помните, что @effects он должен возвращать an Action . Это поведение по умолчанию.

Если вы не уверены, собираетесь ли возвращать новый Action {dispatch: false} параметр use as в декораторе, но помните, что вы должны запускать все отправки вручную.

Подробнее: Как создать эффект ngrx, который не возвращает действие

Ответ №2:

Я думаю, что хороший способ ответить на этот вопрос — сначала взглянуть на EMPTY и NEVER реализации, а затем изучить, как concatMap() (a.k.a mergeMap(() => {}, 1) ) работает.

EMPTY :

 export const EMPTY = new Observable<never>(subscriber => subscriber.complete());
  

и NEVER :

 export const NEVER = new Observable<never>(noop);
  

Возможно, вы уже знакомы с приведенными выше фрагментами. Теперь важно подчеркнуть, как concatMap конкретно mergeMap это работает.

mergeMap принимает функцию обратного вызова, которая определяет, как будут выглядеть внутренние наблюдаемые, и аргумент, который указывает, сколько внутренних наблюдаемых concurrent должно быть активным одновременно. Можно сказать, что внутренняя наблюдаемая активна, если на нее подписана. Он становится неактивным после завершения. Если concurrent достигнуто максимальное значение и вот-вот будет создано новое внутреннее наблюдаемое, внешнее значение, на основе которого

Посмотрев на исходный код

 () => {
  // INNER SOURCE COMPLETE
  // Decrement the active count to ensure that the next time
  // we try to call `doInnerSub`, the number is accurate.
  active--;
  // If we have more values in the buffer, try to process those
  // Note that this call will increment `active` ahead of the
  // next conditional, if there were any more inner subscriptions
  // to start.
  while (buffer.length amp;amp; active < concurrent) {
    const bufferedValue = buffer.shift()!;
    // Particularly for `expand`, we need to check to see if a scheduler was provided
    // for when we want to start our inner subscription. Otherwise, we just start
    // are next inner subscription.
    innerSubScheduler ? subscriber.add(innerSubScheduler.schedule(() => doInnerSub(bufferedValue))) : doInnerSub(bufferedValue);
  }
  // Check to see if we can complete, and complete if so.
  checkComplete();
}

/**
 * Checks to see if we can complete our result or not.
 */
const checkComplete = () => {
  // If the outer has completed, and nothing is left in the buffer,
  // and we don't have any active inner subscriptions, then we can
  // Emit the state and complete.
  if (isComplete amp;amp; !buffer.length amp;amp; !active) {
    subscriber.complete();
  }
};
  

мы можем видеть, что если внутренняя наблюдаемая завершается, внешняя отправит complete уведомление только в том случае, если она была завершена ранее.

Итак, имея это в виду, я бы сказал, что можно вернуться EMPTY , если условие не выполнено. Это просто приведет к завершению внутренней наблюдаемой, не заставляя внешнюю наблюдаемую выдавать какие-либо значения.