tap()vs subscribe()设置一个类属性

时间:2018-03-09 00:27:20

标签: angular typescript rxjs subscribe tap

我对rxjs很新,只是想知道通过管道流并点击它来设置类属性是可以的,或者它应该在订阅中进行。对我来说,无论哪种方式都有效,只是想知道是否可以按照我认为合适的方式进行,或者有一些我不知道的事情。

以两种方式展示的打字稿代码:

export class ViewComponent implements OnInit {

  applicant = {};

  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() {
    this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')])),
      tap(applicant => this.applicant = applicant)
    ).subscribe();
  }
}

VS

export class ViewComponent implements OnInit {

  applicant = {};

  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() {
    this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')]))
    ).subscribe(applicant => this.applicant = applicant);
  }
}

4 个答案:

答案 0 :(得分:17)

好问题。在onChildRemoved运算符的source code中,此评论几乎总结了它:

  

此运算符可用于调试Observables以获取正确的值    或执行其他副作用     注意:这与Observable上的tap不同。如果subscribe返回的Observable未订阅,Observer指定的副作用将永远不会发生。 do因此只是监视现有的执行,它不会像do那样触发执行。

您可以在subscribe中运行的任何副作用也可能放在tap块中。 subscribe表示您主动使用源值的意图,因为它说&#34;当此可观察值发出时,我想将其保存在subscribe变量&#的值中34 ;. applicants运算符主要用于调试,但可以用于运行副作用。

一般情况下,支持tap阻止运行副作用,使用subscribe进行调试,但请注意tap如果需要,可以执行更多操作。

答案 1 :(得分:8)

将observable与其订阅者分开时,

tap非常有用。如果您有一个暴露可观察对象的类,则可以使用subscribe来实现当有人监听到observable时需要执行此类的副作用。另一方面,当您从其他课程订阅时,您可以使用public dummyObservable: Observable<number> = from([1, 2, 3, 4, 5]).pipe( // Side effects, executed every time I emit a value // I don't know which side effects implements who subscribes to me tap( n => console.log("I'm emitting this value:", n) ) ); 从订阅者的角度实施副作用。

具有可观察性的类:

ngOnInit(): void {
  this.dummyService.dummyObservable.subscribe(
    // Side effects, executed every time I receive a value
    // I don't know which side effects implements the observable
    data => console.log("I'm receiving this value: ", data)
  );
}

订阅类:

$.ajax({
            type: 'post',
            url: 'include/ajax.php',
            data: $('#form').serialize(),
            success: function (response) {
                // do something
            },
            error: function(jqxhr,textStatus,errorThrown){
                console.log(jqxhr);
                console.log(textStatus);
                console.log(errorThrown);
            }
        }); 

答案 2 :(得分:2)

使用 AsyncPipeNgrxPushPipe

对于那些想使用 async 管道或 ngrxPush 管道的人,您别无选择,只能使用 tap 运算符

在上面的例子中,我们可以有类似的东西

export class ViewComponent implements OnInit {
  
  applicant = {};
  applicant$ = this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')])),
      tap(applicant => this.applicant = applicant)
    )
  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() { }
}

在 html 中

使用 AsyncPipe

<ng-container *ngIf='applicant$ | async'>

   ...Some Html code here

</ng-container>

使用 NgrxPushPipe(请记住,这只适用于您 import { ReactiveComponent } from '@ngrx/component'

<ng-container *ngIf='applicant$ | ngrxPush'>
 
...Some Html code here

</ng-container>

补充

以上两个管道有助于提高代码的可维护性并减少/消除由于未订阅的可观察对象而导致的内存泄漏风险

上面的代码可以简化为

TS 文件

export class ViewComponent {
 
  applicant$ = this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => 
        state.applicants.entities[params.get('id')])
      )
    )
  constructor(public route: ActivatedRoute, private store: Store<any>) {}

}

HTML 文件

使用 AsyncPipe

<ng-container *ngIf='applicant$ | async as applicant'>

   ...Some Html code here

</ng-container>

使用 NgrxPushPipe

<ng-container *ngIf='applicant$ | ngrxPush as applicant'>

   ...Some Html code here

</ng-container>

使用 NgrxPushPipe

<ng-container *ngIf='applicant$ | ngrxPush as applicant'>

   ...Some Html code here

</ng-container>

使用 ngrxLet 结构指令(也来自“@ngrx/component”)

<ng-container *ngrxLet='applicant$; let applicant'>

   ...Some Html code here

</ng-container>

答案 3 :(得分:0)

Michael Hladky建议您将所有副作用都放到tap运算符中,然后他解释为什么here

我认为这样做通常是一个好主意,因为正如迈克尔所说,您可以将许多可观测对象合并在一起,并为所有可观测对象创建一个订阅。

我不知道这样做是否可以提高性能,但是当您希望仅退订一个订阅时,它无疑会变得更容易。 取消订阅是您始终应该执行的操作,以避免可能的内存泄漏或其他奇怪行为。

此方法的另一个好处是,您可以轻松地暂停,恢复或完成一组可观察对象,方法是通过操作符(例如filter或takeWhile)将其管道传递,或通过将其映射到另一个可观察对象,例如:

const allMergedObservables$ = merge(obs1, obs2, obs3);
const play$ = new Subject();

play$.asObservable().pipe(
    switchMap(bool => bool ? allMergedObservables$ : EMPTY)
).subscribe();

// Putting 'true' into the play stream activates allMergedObservables$. 
play$.next(true);

// Something happens that makes you want to pause the application,
// for instance the user opens the print dialog box,
// so you issue 'false' in the play stream which in turn stops the
// inner subscription of allMergedObservables$:
play$.next(false);

但是,这取决于您以及您喜欢哪种编程样式。