Сохранение информации об ошибках и внешнем наблюдаемом - PullRequest
1 голос
/ 17 июня 2020

Чтобы гарантировать, что ошибка не завершает внешнее наблюдаемое, я использовал общий шаблон rx js эффектов :

 public saySomething$: Observable<Action> = createEffect(() => {

    return this.actions.pipe(

      ofType<AppActions.SaySomething>(AppActions.SAY_SOMETHING),

      // Switch to the result of the inner observable.
      switchMap((action) => {
        // This service could fail.
        return this.service.saySomething(action.payload).pipe(
          // Return `null` to keep the outer observable alive!
          catchError((error) => {
            // What can I do with error here?
            return of(null);
          })
        )
      }),

      // The result could be null because something could go wrong.
      tap((result: Result | null) => {
        if (result) {
          // Do something with the result!
        }
      }),

      // Update the store state.
      map((result: Result | null) => {
        if (result) {
          return new AppActions.SaySomethingSuccess(result);
        }
        // It would be nice if I had access the **error** here. 
        return new AppActions.SaySomethingFail();
      }));
});

Обратите внимание, что я использую catchError на внутреннем наблюдаемом, чтобы поддерживать внешнее наблюдаемое, если базовый сетевой вызов терпит неудачу (service.saySomething(action.payload)):

catchError((error) => {
  // What can I do with error here?
  return of(null);
})

Последующие операторы tap и map учитывают это в своих подписях, разрешая null, т.е. (result: Result | null). Однако я теряю информацию об ошибке. В конечном итоге, когда последний метод map возвращает new AppActions.SaySomethingFail();, я теряю любую информацию об ошибке.

Как я могу сохранить информацию об ошибке по всему каналу, а не терять ее в момент обнаружения?

Ответы [ 2 ]

0 голосов
/ 18 июня 2020

Я бы не стал пытаться хранить информацию об ошибках по всей трубе. Вместо этого вы должны отделить свой конвейер успеха (tap, map) от конвейера ошибок (catchError), добавив все операторы к наблюдаемому, результат которого они должны фактически использовать, т.е. ваш внутренний наблюдаемый.

public saySomething$: Observable<Action> = createEffect(() => {

    return this.actions.pipe(
      ofType<AppActions.SaySomething>(AppActions.SAY_SOMETHING),
      switchMap((action) => this.service.saySomething(action.payload).pipe(
        tap((result: Result) => {
          // Do something with the result!
        }),
        // Update the store state.
        map((result: Result) => {
          return new AppActions.SaySomethingSuccess(result);
        }),
        catchError((error) => {
          // I can access the **error** here. 
          return of(new AppActions.SaySomethingFail());
        })
      )),     
    );
});

Таким образом, tap и map будут выполняться только при успешных результатах от this.service.saySomething. Переместите все побочные эффекты ошибок и отображение ошибок в catchError.

0 голосов
/ 17 июня 2020

Как было предложено в комментариях, вы должны использовать Функция защиты типов

К сожалению, я не могу запускать машинописный текст во фрагменте, поэтому я прокомментировал типы

const { of, throwError, operators: {
    switchMap,
    tap,
    map,
    catchError
  }
} = rxjs;

const actions = of({payload: 'data'});

const service = {
  saySomething: () => throwError(new Error('test'))
}

const AppActions = {
}

AppActions.SaySomethingSuccess = function () {
}
AppActions.SaySomethingFail = function() {
}

/* Type guard */
function isError(value/*: Result | Error*/)/* value is Error*/ {
  return value instanceof Error;
}

const observable = actions.pipe(
  switchMap((action) => {
    
    return service.saySomething(action.payload).pipe(
      catchError((error) => {
        return of(error);
      })
    )
  }),
  tap((result/*: Result | Error*/) => {
    if (isError(result)) {
      console.log('tap error')
      return;
    }
    
    console.log('tap result');
  }),
  map((result/*: Result | Error*/) => {
    if (isError(result)) {
      console.log('map error')
      return new AppActions.SaySomethingFail();
    }
    
    console.log('map result');
    return new AppActions.SaySomethingSuccess(result);
  }));
  
  observable.subscribe(_ => {

  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
...