关于angular:RxJS 6-取消/结束管道

RxJS 6 - Cancel / End a Pipe

使用新版本的RxJS 6,尤其是管道运算符。当前,使用管道获取API调用的结果,并将其传递给一系列其他任务。

一切正常,但在遇到问题时似乎无法找到取消或终止管道的方法。例如,我正在使用tap运算符检查该值是否为null。然后,我抛出一个错误,但是管道仍然似乎移至下一个任务,在本例中为concatmap。

因此,您如何过早结束或取消管道?预先感谢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getData(id: String): Observable {
return this.http.get(`${this.baseUrl}/path/${id}`).pipe(
   tap(evt => {
    if (evt == null) {
      return throwError(new Error("No data found..."));
    }
  }),
concatMap(
  evt =>
     <Observable>(
        this.http.get(
    `${this.baseUrl}/path/relatedby/${evt.child_id}`
      ).map(res =>( {"response1":evt,"response2":res}) )
 )
),
retry(3),
catchError(this.handleError("getData", []))
);}

我从这种堆叠闪电战中尝试了基本概念,并且奏效了。它取消了剩余的操作。请参阅下面的链接。

https://stackblitz.com/edit/angular-4ctwsd?file=src/app/app.component.ts

我在您的代码和我的代码之间看到的区别是,我使用的是throw而不是throwError(这是您写的东西吗?),而我只是抛出错误……不返回引发的错误。 >

以下是参考代码:

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
import { Component } from '@angular/core';
import { of, from } from 'rxjs';
import { map, catchError, tap, retry} from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  name = 'Angular 6';

  constructor() {
    of('a', 'b', 'c', 'd')
      .pipe(
       map(x => {
        if (x === 'c') {
          throw 'An error has occurred';
        }
        return x;
       }),
       tap(x => console.log('In tap: ', x)),
       retry(3),
       catchError(() => of('caught error!'))
      )
      .subscribe(x => console.log(x));
  }
}

RXJS会抛出错误并停止执行。您在管道中应用的操作员是错误的选择。

您正在使用的'tap'运算符仅用于施加副作用,即对DOM,console.log进行更改,或更改组件上已获取的某些变量的值(即this.counter = someValue)。 tap运算符并不是要更改RXJS"流",它只会返回与接收到的相同的可观察值。 https://rxjs-dev.firebaseapp.com/api/operators/tap

另一方面,在流上起作用的运算符(例如" map")可能会引发错误。看到这个stackblitz

总的来说,代码是

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
getData(): Observable {
    return this.httpGet('basePath').pipe(
      map(evt => {
        if (evt === null) {
          return throwError(new Error("No data found..."));
        } else {
          return evt;
        }
      }),
      concatMap(evt => {
        return this.httpGet(`childPath/${evt.serverResult}`);
      }),
      map(res => {
        return {
         "response2": res.serverResult
        };
      }),
      retry(3),
      catchError(error => {
        console.log('error handled!');
        return of(null);
      })
    )
  }

  private httpGet(someString: string): Observable<{ serverResult: number }> {
    return timer(1000).pipe( // fake a server call that takes 1 second
      map(_ => { // fake a server result
      // Comment out the valid return and uncomment the null return to see it in action
        //return null;
        return {
          serverResult: 1
        }
      })
    );
  }

如果您不想将错误转换为流中的有效值,请在订阅函数的错误回调中进行处理。


您还可以使用信号Subject和rxjs运算符取消/结束管道:takeUntil

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
httpGetSafe(path: string): Observable<Data> {
  const stopSignal$ = new Subject();

  return this.http.get<Data>(path).pipe(
    map(data => {
      const isBad = data === null;
      if (isBad) {
        stopSignal$.next();
      }
      return data;
    }),
    takeUntil(stopSignal$)
  );
}

有时候,它比抛出错误更简单,更灵活...


可观察的函数总是内部package在try / catch块中。流中抛出的任何错误都将终止流,并向订阅者或操作员调用任何错误回调。

这里的问题是throwError()。我不知道该函数是什么,也无法将其识别为Observable运算符,但看起来它正被用作一个(并且从未订阅过)。

tap通常仅用于副作用,因为它完全无法影响流中的值。但是,正如我之前在try / catch块中提到的那样,您应该只能够throw一个新的Error,流将处理其余的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getData(id: String): Observable {
return this.http.get(`${this.baseUrl}/path/${id}`).pipe(
   tap(evt => {
    if (evt == null) {
      throw new Error("No data found...");
    }
  }),
concatMap(
  evt =>
     <Observable>(
        this.http.get(
    `${this.baseUrl}/path/relatedby/${evt.child_id}`
      ).map(res =>( {"response1":evt,"response2":res}) )
 )
),
retry(3),
catchError(this.handleError("getData", []))
);}