Edit: Prior to RxJs 6, tap
was called as do
. Updated title to reflect tap too.
I would like to understand what is the best practice of using .subscribe
and .do
methods of Observables.
For example if I need to do some job after initial data is loaded from server
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
});
I can do the same with .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
});
Which one is better and why?
The pipe method is for chaining observable operators, and the subscribe is for activating the observable and listening for emitted values. The pipe method was added to allow webpack to drop unused operators from the final JavaScript bundle. It makes it easier to build smaller files.
Some common synonyms of subscribe are accede, acquiesce, agree, assent, and consent.
The map operator will simply apply a function to that data and return the result. The tap operator however takes a data, apply a function to that data but returns the original data, if the function bothered to return a result, tap just ignores it.
RxJS tap() operator is a utility operator that returns an observable output that is identical to the source observable but performs a side effect for every emission on the source observable.
Edit: For RxJS 6 or above, Read do
as tap
.
do
is used for side-effects.subscribe
is used to invoke an observable. Replacingdo
withsubscribe
creates undesired results. Replacingsubscribe
withdo
will not even invoke the stream.
Consider these examples :
Using subscribe :
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');
});
The output of the above code is
"some heavy task, may be network call "
"action 1"
"some heavy task, may be network call "
"action 2"
You can see the Rx.Observable.create
got executed twice. Our goal is do it only once but along with action 2, do action 1 also.
Using do:
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');
});
The output would be
"some heavy task, may be network call "
"action 1"
"action 2"
This is what we actually wanted. We need 'action 2' but before that do 'action 1' also.
Why is it called side effect:
Because it will not affect the flow of stream unlike other operators. It takes the response , does something and even if it modifies the response the stream is going to ignore it . For ex:
testObservable
.do(function(res){
console.log('action 1');
return res+'some other text';
})
.subscribe(function(res){
console.log('action 1');
});
The above code will still give the same output as before. So no matter what you execute in do
the stream is going to ignore it and proceed with its execution.
If we are doing pure 'functional reactive programming' we don't want any side effects in the stream. So,
do
is discouraged and mostly used only for debugging purposes .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With