Angular 9/rxjs: как справиться с ошибкой внутри switchMap?

Я использую Bootstrap (6.1.0) TypeAhead с питанием от Angular (9) и определяю его функцию поиска следующим образом:

search = (text$: Observable<string>) => {
    return text$.pipe(
      debounceTime(200),
      distinctUntilChanged(),
      // switchMap allows returning an observable rather than maps array
      switchMap((searchText) => {
        if (!searchText || searchText.trim().length == 0) {
          // when the user erases the searchText
          this.dealerRepUserID = 0;
          this.dealerRepChanging.emit(this.dealerRepUserID);
          return EMPTY;
        }
        else if (this.dealerID == this.hostOrganizationID) {
          // get a list of host reps
          return this.myService.getHostRepsAutoComplete(searchText, this.includeInactive);
        } else {
          // get a list of dealer reps
          return this.myService.getDealerReps(this.dealerID, searchText);
        }
      })
    );
  }

Функция должна возвращать Observable. Как мне поймать ошибку, возникающую внутри switchMap?


person CAK2    schedule 25.06.2020    source источник
comment
Это смотря какая ошибка. Вы можете использовать try-catch для всего блока или вы можете перехватывать error уведомления, испускаемые this.myService.get* вызовами с оператором catchError(), в зависимости от того, что вы хотите сделать. Или вы также можете поставить catchError() после switchMap.   -  person martin    schedule 25.06.2020


Ответы (2)


Вы пробовали catchError

import { catchError } from 'rxjs/operators';
return text$.pipe(
      debounceTime(200),
      distinctUntilChanged(),
      // switchMap allows returning an observable rather than maps array
      switchMap((searchText) => {
        if (!searchText || searchText.trim().length == 0) {
          // when the user erases the searchText
          this.dealerRepUserID = 0;
          this.dealerRepChanging.emit(this.dealerRepUserID);
          return EMPTY;
        }
        else if (this.dealerID == this.hostOrganizationID) {
          // get a list of host reps
          return this.myService.getHostRepsAutoComplete(searchText, this.includeInactive).pipe(catchError(error => of());
        } else {
          // get a list of dealer reps
          return this.myService.getDealerReps(this.dealerID, searchText).pipe(catchError(error => of());
        }
      })
    );

Вот мой эффект приложения

public loadDataPerformance$: Observable<Action> = createEffect(() => {
    return this.actions$.pipe(
      ofType(RiskProfileActions.loadDataPerformance),
      withLatestFrom(
        this.store$.select(fromRoot.getAnalyticsFilterSelectedOptions),
        this.store$.pipe(select(fromFactoryPerformance.getFactoryId))
      ),
      switchMap(([{ recordDate }, filters, factoryId]) =>
        this.riskProfileApiService.getDataPerformanceData(filters, factoryId, recordDate).pipe(
          map((riskDataPerformanceData: PerformanceDataModel) =>
            RiskProfileActions.loadRiskScoreBreakdownPerformanceSuccess(riskDataPerformanceData)
          ),
          catchError(error => of(RiskProfileActions.loadRiskScoreBreakdownPerformanceFail(error)))
        )
      )
    );
  });

person Phat Tran Ky    schedule 25.06.2020

switchMap сам по себе не вызовет никакой ошибки, вещь, которая может сделать что-то неожиданное, - это возвращенные наблюдаемые this.myService.getHostRepsAutoComplete и this.myService.getDealerReps. Сложный момент с отловом ошибок заключается в том, что всякий раз, когда есть error, наблюдаемая, которая выдает ошибку, уничтожается.

Например

observable$.pipe(
 switchMap(() => observable2$),
 catchError(() => doSomethingFunction())
).subscribe()

observable$ будет завершено, как только произойдет ошибка, которая завершит ваш поисковый поток, и вы больше не получите данных после ошибки.

Как показал Phat Tran Ky на своем примере, обработка ошибок должна происходить внутри новых потоков в операторе switchMap

observable$.pipe(
 switchMap(() => observable2$.pipe(catchError(() => doSomethingFunction())),
 )
).subscribe()

Поступая таким образом, всякий раз, когда возникает ошибка изнутри, он убивает внутренний наблюдаемый объект (observable2$), но не убивает внешние подписки на внешний наблюдаемый объект observable$

Еще одно усовершенствование, которое вы можете сделать для обработки ваших ошибок в одном месте, может состоять в том, чтобы объединить внутреннюю наблюдаемую в одну, например, что-то вроде

observable$.pipe(
 switchMap(() => {
   return merge(
   observable1$.pipe(filter(() => ${your if else condition for case 1})),
   observable2$.pipe(filter(() => ${your if else condition for case 2})),
   observable3$.pipe(filter(() => ${your if else condition for case 3})),
   ).pipe(catchError((error) => yourErrorHandlerFunction(error)))
  })),
 )
).subscribe()
person Християн Христов    schedule 25.06.2020