编辑:在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
});
哪一个更好,为什么?
答案 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)
我在tap
(do
)中进行了所有操作,因为它允许返回可观察到的值,并随后与其他管道结合使用。例如,您可以在服务中使用tap
,并始终返回observable作为结果。它为您提供了更大的灵活性,可根据需要对其进行处理,如果明天您决定在此添加更多内容,则无需重构订阅Tap。最后,我仅在最后一刻(渲染之前)仅使用.subscribe()
(括号中没有内容)来订阅。
而且,它使代码保持一致-因此,您知道所有副作用始终在tap
中,而不是“轻按而订阅中的某些东西”。
P.S。如果您使用Angular,通常可以在视图中使用| async
管道,该管道会自动订阅,因此使用.tap而不是.subscribe是副作用的另一个原因。