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

bul*_*rce 21 tap subscribe rxjs typescript angular

我是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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

vin*_*nce 22

好问题.在运算符的源代码tap,这个注释几乎总结了它:

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

您可以在一个罐中运行的任何副作用tap也可能被放入subscribe块中.这subscribe表示您主动使用源值的意图,因为它说"当这个可观察的发出时,我想将它的值保存在applicants变量中".该tap运营商主要是那里调试,但它可以被用来运行副作用.

一般来说,有利于subscribe块运行副作用,tap用于调试,但要注意,tap如果你需要它可以做更多.

  • 我完全不同意。所有可观察量都应通过水龙头设置副作用,以便可以通过附加管道返回并进一步修改它们。一旦你强制订阅,你就失去了进一步控制可观察对象的所有能力。恕我直言,订阅应该始终为空。 (10认同)
  • 当我需要在可观察对象上添加强制性副作用时,我会使用tap。无论如何订阅,它们都将始终激活 (2认同)
  • 我同意使用 subscribe 作为运行副作用的默认位置的一般想法,但是有时需要点击,因为可观察的最终结果可能不再包含您想要的副作用值。例如,如果其中一个运算符是“减少”或“映射”,则您要查找的值可能会消失。 (2认同)

mar*_*ogl 14

tap当您将observable与其订阅者分开时,此选项非常有用.如果您有一个暴露可观察对象的类,您可以使用它tap来实现当有人正在监听 observable 时需要执行此类的副作用.另一方面,当您从另一个类订阅它时,您可以使用从订阅者的角度实现副作用subscribe.

具有可观察性的类:

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) )
);
Run Code Online (Sandbox Code Playgroud)

订阅类:

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)
  );
}
Run Code Online (Sandbox Code Playgroud)


Fre*_*ond 10

Michael Hladky 建议您将所有副作用都放在 tap 运算符中,他在这里解释了原因。

我认为这样做通常是一个好主意,因为正如 Michael 所说,然后您可以将许多 observable 合并在一起并为所有这些创建一个单一的订阅。

我不知道这是否会提高性能,但是当您想取消订阅而只订阅一个时,它肯定会让事情变得更容易。取消订阅是您始终应该做的事情,以避免可能的内存泄漏或其他奇怪的行为。

这种方法的另一个好处是,您可以轻松地暂停、恢复或完成一组 observable,方法是通过 filter 或 takeWhile 等运算符对其进行管道传输,或者通过另一个像这样的 observable 切换映射:

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);
Run Code Online (Sandbox Code Playgroud)

但是,这取决于您和您喜欢的任何编程风格。


Owe*_*vin 5

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() { }
}
Run Code Online (Sandbox Code Playgroud)

并在html中

使用AsyncPipe

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() { }
}
Run Code Online (Sandbox Code Playgroud)

使用NgrxPushPipe(记住这只有在你的情况下才有效import { ReactiveComponent } from '@ngrx/component'

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

   ...Some Html code here

</ng-container>
Run Code Online (Sandbox Code Playgroud)

补充内容

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

上面的代码可以简化为

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>) {}

}
Run Code Online (Sandbox Code Playgroud)

HTML 文件

使用AsyncPipe

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

</ng-container>
Run Code Online (Sandbox Code Playgroud)

使用NgrxPushPipe

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>) {}

}
Run Code Online (Sandbox Code Playgroud)

使用NgrxPushPipe

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

   ...Some Html code here

</ng-container>
Run Code Online (Sandbox Code Playgroud)

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

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

   ...Some Html code here

</ng-container>
Run Code Online (Sandbox Code Playgroud)