关于angular:RxJS mergeMap运算符内部的错误处理

Error handling inside RxJS mergeMap operator

当我使用Angular HttpClient发出GET请求时,我得到一个可观察到的结果,并在RxJS运算符mergeMap中对其进行处理。

现在,发生了一次又一次抛出404的错误,我想抓住它。 最后,浏览器控制台中不应出现任何错误消息,并且应使用流的下一个值来处理管道。

有可能吗? 我没有用catchError()管理它。

这是我的代码的简化版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    ...
    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()添加方法

我以这种方式进行了尝试,但是未检测到错误,并且下一个mergeMap不起作用(IDE不再识别诸如Thing.id,Thing.Type,Thing.img_ref之类的参数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
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) => {
          ...


您将需要使用retryretryWhen(名称非常不言自明)-这些操作符将重试失败的订阅(一旦发出错误,则可观察到源)。

要在每次重试时提高id,您可以将其锁定在范围内,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)
    )
  );
}

1
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js">

请注意,最好有条件的retry仅在404错误时重试。 这可以通过retryWhen来实现,例如

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

请查看有关rxjs中的错误处理的本文,以使retryretryWhen更加丰富。

希望这可以帮助

更新:还有其他方法可以实现这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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)
    )
  );
}

1
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js">