do(tap)vs subscribe

时间:2017-07-26 08:32:57

标签: javascript angular rxjs

编辑:在RxJs 6之前,tap被称为do。更新的标题也反映了点击。

我想了解使用Observables的.subscribe.do方法的最佳做法。

例如,如果我需要在从服务器加载初始数据后做一些工作

const init$: Observable<MyData> = this._dataService.getData();

init$
  .do((initialData: MyData) => {
    this.data = initialData; // at this step I am initializing the view
  })
  .switchMap(() => loadExtraData)
  .subscribe((extraData) => {
     doSomething(extraData, this._data); // I need this._data here
  }); 

我可以对.subscribe

做同样的事情
const init$: Observable<MyData> = this._dataService.getData()
  .shareReplay(1);

init$
  .subscribe((initialData: MyData) => {
    this.data = initialData; // at this step I am initializing the view
  })

init$
  .combineLatest(loadExtraData)
  .subscribe(([initialData, extraData]) => {
     doSomething(extraData, initialData); // I need this._data here
  }); 

哪一个更好,为什么?

4 个答案:

答案 0 :(得分:19)

编辑:对于RxJS 6或更高版本,请将do读为tap

  

do用于副作用。 subscribe用于调用可观察对象。用do替换subscribe会产生不良后果。用subscribe替换do甚至不会调用流。

请考虑以下示例:

使用订阅

const testObservable = Rx.Observable.create(function(observer){
   console.log('some heavy task, may be network call ');
   observer.next('success');
});

testObservable.subscribe(function(res){
    console.log('action 1');
});

testObservable.subscribe(function(res){
   console.log('action 2');
});

上述代码的输出是

"some heavy task, may be network call "
"action 1"
"some heavy task, may be network call "
"action 2"

你可以看到Rx.Observable.create被执行了两次。我们的目标是只做一次,但与行动2一起,也行动1

使用执行

const testObservable = Rx.Observable.create(function(observer){
   console.log('some heavy task, may be network call ');
   observer.next('success');
});

testObservable
    .do(function(res){
        console.log('action 1');
    })  
    .subscribe(function(res){
        console.log('action 2');
    });

输出为

"some heavy task, may be network call "
"action 1"
"action 2"

这就是我们真正想要的。我们需要采取行动2&#39;但在此之前,请采取措施1&#39;也。

为什么称为副作用

因为与其他运营商不同,它不会影响流的流量。它接受响应,做某事,即使它修改了流将忽略它的响应。例如:

testObservable
    .do(function(res){
        console.log('action 1');
        return res+'some other text';
    })  
    .subscribe(function(res){
        console.log('action 1');
    });

上面的代码仍会提供与以前相同的输出。所以无论你在do中执行什么,流都会忽略它并继续执行它。

  

如果我们正在进行纯粹的功能性反应式编程&#39;我们不希望在流中出现任何副作用。因此,不鼓励使用do,并且主要仅用于调试目的。

答案 1 :(得分:3)

在使用subscribe()在运算符之间建立连接之前,Observable链不会做任何事情。所以你总是必须使用subscribe()方法。

do()运算符仅用于产生副作用。

根据我的经验,通常无法在subscribe()中执行所有操作,有时您需要使用do(),因为您的应用程序逻辑需要按特定顺序执行操作。

因此,一般情况下,我会尽量避免使用do()并将大部分逻辑放入subscribe()。只有当我必须以不同的顺序执行操作时,我才会使用do()

答案 2 :(得分:3)

在任何情况下,您都很难在服务中使用subscribe。如果您的数据流以真正的反应方式完成,那么您只能订阅消费者(由Angular中的组件表示)来运行整个流程shebang。这允许流组成,其是反应方法的基础。避免将观察者的某些东西保存到局部范围内,这也违背了流的纯度。您可能宁愿撰写流以传递所有必要的数据或组合流。

答案 3 :(得分:0)

我在tapdo)中进行了所有操作,因为它允许返回可观察到的值,并随后与其他管道结合使用。例如,您可以在服务中使用tap,并始终返回observable作为结果。它为您提供了更大的灵活性,可根据需要对其进行处理,如果明天您决定在此添加更多内容,则无需重构订阅Tap。最后,我仅在最后一刻(渲染之前)仅使用.subscribe()(括号中没有内容)来订阅。 而且,它使代码保持一致-因此,您知道所有副作用始终在tap中,而不是“轻按而订阅中的某些东西”。

P.S。如果您使用Angular,通常可以在视图中使用| async管道,该管道会自动订阅,因此使用.tap而不是.subscribe是副作用的另一个原因。