Обработка ошибок внутри оператора RxJS mergeMap - PullRequest
0 голосов
/ 17 апреля 2019

Когда я делаю GET-запрос с использованием Angular HttpClient, я получаю наблюдаемый результат и обрабатываю его в операторе mergeMap RxJS.

Теперь случается, что снова и снова выбрасывается 404, что я хотел быловить.Наконец, в консоли браузера не должно появляться сообщений об ошибках, и канал должен обрабатываться со следующим значением потока.

Есть ли возможность для этого?Я не справился с этим с помощью catchError ().

Вот упрощенная версия моего кода:

    ...
    this.service1.getSomeStuff().pipe(
          mergeMap((someStuff) => {
            return from(stuff);
          }),
          mergeMap((stuff) => {
            return this.service2.getMoreStuff(stuff.id); // Here I need some error handling, if 404 occurs
          }),
          mergeMap((things) => {
            return from(things).pipe(
              mergeMap((thing) => {
                if (allLocations.some(x => x.id === metaData.id)) {
                  return this.service2.getMore(thing.id, thing.type, thing.img_ref);
                }
              }),
              map((thing) => {
              ...

ОБНОВЛЕНИЕ: Добавлен подход с catchError ()

Я пробовал таким образом, но ошибка не обнаружена, и следующая таблица слияния не работает (IDE не распознает такой параметр, как thing.id, thing.type, thing.img_ref больше):

...
this.service1.getSomeStuff().pipe(
      mergeMap((someStuff) => {
        return from(stuff);
      }),
      mergeMap((stuff) => {
        return this.service2.getMoreStuff(stuff.id).pipe(
          catchError(val => of(`Error`))
        );
      }),
      mergeMap((things) => {
        return from(things).pipe(
          mergeMap((thing) => {
            if (allLocations.some(x => x.id === metaData.id)) {
              return this.service2.getMore(thing.id, thing.type, thing.img_ref);
            }
          }),
          map((thing) => {
          ...

1 Ответ

2 голосов
/ 17 апреля 2019

Вам нужно будет использовать retry или retryWhen (имена довольно очевидны) - эти операторы будут повторять неудачную подписку (повторно подписаться на наблюдаемый источник, как только появится ошибка.

Чтобы поднять id при каждой повторной попытке - вы можете заблокировать его в области видимости, например:

const { throwError, of, timer } = rxjs;
const { tap, retry, switchMap } = rxjs.operators;

console.log('starting...');

getDetails(0)
  .subscribe(console.log);


function getDetails(id){
  // retries will restart here
  return of('').pipe(
    switchMap(() => mockHttpGet(id).pipe(
      // upon error occurence -- raise the id
      tap({ error(err){
        id++;
        console.log(err);
      }})
    )),  
    retry(5) // just limiting the number of retries
             // you could go limitless with `retry()`
  )
}

function mockHttpGet(id){
  return timer(500).pipe(
    switchMap(()=>
      id >= 3
      ? of('success: ' + id)
      : throwError('failed for ' + id)
    )
  );
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>

Обратите внимание, что было бы разумнее иметь условную retry, чтобы повторить попытку только при 404 ошибках. Это может быть достигнуто с помощью retryWhen, например

// pseudocode
retryWhen(errors$ => errors$.pipe(filter(err => err.status === '404')))

Ознакомьтесь с этой статьей об обработке ошибок в rxjs , чтобы стать более состоятельными с retry и retryWhen.

Надеюсь, это поможет


ОБНОВЛЕНИЕ : есть и другие способы достижения этого:

const { throwError, of, timer, EMPTY } = rxjs;
const { switchMap, concatMap, map, catchError, take } = rxjs.operators;

console.log('starting...');

getDetails(0)
  .subscribe(console.log);


function getDetails(id){
  // make an infinite stream of retries
  return timer(0, 0).pipe(
    map(x => x + id),
    concatMap(newId => mockHttpGet(newId).pipe(
      // upon error occurence -- suppress it
      catchError(err => {
        console.log(err);
        // TODO: ensure its 404

        // we return EMPTY, to continue
        // with the next timer tick
        return EMPTY;
      })
    )),
    // we'll be fine with first passed success
    take(1)
  )
}

function mockHttpGet(id){
  return timer(500).pipe(
    switchMap(()=>
      id >= 3
      ? of('success: ' + id)
      : throwError('failed for ' + id)
    )
  );
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...