Reputation: 9467
I am using latest angular 8 and am new to the concept of observables. Question I have if I am directly calling an observable and not apply it to a subscription variable, do I still need to unsubscribe. Below are the scenarios I would like to know if I need to unsubscribe on? Many thanks in advance
Scenario 1 - Calling a httpService from a component:
Service - httpService
getContactsHttp(){
let headers: any = new HttpHeaders(this.authService.getHeadersClient());
return this.httpClient.get('/contacts', {headers: headers})
.pipe(timeout(this.authService.getTimeoutLimit('normal')));
}
Component - Calling getContactsHttp and sorting response
getContacts() {
this.httpService.getContactsHttp().subscribe((data:any[])=>{
this.records = this.sortData(data)
})
}
Scenario 2 - on an observable susbcribed in a component
contacts$: new Subject<any[]>;
ngOnInit() {
this.getContacts();
this.contacts$.subscribe((data:any[])=>{
this.records = this.sortData(data);
})
}
getContacts() {
this.httpService.getContactsHttp().subscribe((data:ContactSearch[])=>{
this.contacts$.next(data);
})
}
Service - httpService
getContactsHttp(){
let headers: any = new HttpHeaders(this.authService.getHeadersClient());
return this.httpClient.get('/contacts', {headers: headers})
.pipe(timeout(this.authService.getTimeoutLimit('normal')));
}
Upvotes: 4
Views: 6166
Reputation: 7351
1) Generally, you don't need to unsubscribe when calling an http call directly. Even if the component get's destroyed, the overhead with the subscription finishing after the destruction is insignificant. You'd need to unsubscribe here if switching your components rapidly. Also unsubscribing cancels the http request, so if that's desired, then unsubscribe.
Unsubscribing does not do any harm. If you're not sure, always unsubscribe.
2) You do need to unsubscribe when subscribing to an observable that does not complete when your compoment gets destroyed. Otherwise this would cause a memory (and performance) leak. Because the observable itself holds a reference to the subscription and the subscription holds the reference to the component, the component will never get cleared from the memory and the action described in the subscription will be running until the observable completes, which in your case is never. That will happend for every instance of your component.
I'll share two popular options on simplifying the burden of unsubscribing. Expanding on the @amanagg1204 answer, you could create a base component from which you'd extend all of your future components. You can have a custom operator in it. There is one downside to that - you always have to call super.ngOnDestroy()
if you need to use ngOnDestroy
in your component.
import { OnDestroy } from "@angular/core";
import { Subject, MonotypeOperatorFunction } from "rxjs";
import { takeUntil } from "rxjs/operators";
export abstract class UnsubscribeComponent implements OnDestroy {
protected destroyed$: Subject<void> = new Subject();
ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
takeUntilDestroyed<T>(): MonoTypeOperatorFunction<T> {
return takeUntil(this.destroyed$);
}
}
export class Component extends UnsubscribeComponent {
ngOnInit() {
this.contacts$.pipe(
this.takeUntilDestroyed(),
).subscribe((data:any[])=>{
this.records = this.sortData(data);
});
}
// WARNING - if you declare your ngOnDestroy in the component
ngOnDestroy() {
// DO NOT FORGET to call this
super.ngOnDestroy();
doYourStuff();
}
}
Other option (my prefered) is not to have a parent abstract class (although it could also be implemented like that), but to use an utility called subsink
(npm i subsink --save
)
import { SubSink } from 'subsink';
export class SampleComponent implements OnInit, OnDestroy {
private subs = new SubSink();
ngOnInit(): void {
// Just put it into sink.
this.subs.sink = this.contacts$.subscribe((data:any[])=>{
this.records = this.sortData(data);
});
// call repeatedly
this.subs.sink = this.otherService$.subscribe((data:any[])=>{
this.things = this.sortData(data);
});
}
ngOnDestroy(): void {
// this will unsubscribe all
this.subs.unsubscribe();
}
}
Upvotes: 4
Reputation: 92
As many have already pointed out, http returns a Cold Observable but you should still unsubscribe from the observable.I will suggest a better way of managing unsubscription so that you don't have to manually add a ngOnDestroy() lifecycle hook every time. I would start by creating an unsubscribe component as below
import { OnDestroy } from "@angular/core";
import { Subject } from "rxjs";
export abstract class UnsubscribeComponent implements OnDestroy {
protected destroyed$: Subject<void> = new Subject();
ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
}
And then in extend it in every component class declaration (wherever required)
export class ABC extends UnsubscribeComponent
Within the constructor call super()
constructor(your dependencies) {
super()
}
and finally with your subscriptions, do something like below
this.obs$.pipe(takeUntil(this.destroyed$)).subscribe(() => {
// some logic here
})
Another thing is takeUntil should be last operator in the pipe. Hope this helps you.
Upvotes: 0
Reputation: 5522
Yes always unsubscribe. You have multiple ways of unsubscribing, which are the following:
-the use takeUntil()
-the take(1)
-unsubscribe()
in the ngOnDestroy()
-using the async
pipe
Yes, the httpClient returns a cold observable, but this question is going to explain you why you should still unsubscribe
. (take a look to the second answer with highest votes)
Is it necessary to unsubscribe from observables created by Http methods?
https://blog.angularindepth.com/why-you-have-to-unsubscribe-from-observable-92502d5639d0
On the side note:
1) Never subscribe in the service. We are in the Angular world and we need to think in a reactive way, buy subscribing in the service prevents you from making use of that observable in case you want to combine it with something else.
2) Start using the declarative approach when working with observables.
3) Stop using any
, it's not good practice. Make the use of classes and interfaces, by doing so your code will be more readable.
Benefits from declarative approach: -Leverage the power of RxJs observables and operators -Effectively combine streams -Easy share observables -Readily react to user action
You maybe wondering how does a declarative approach looks like?
Instead of having methods for returning observables, you are going to do this.
SERVICE.TS
yourObservableName$ = this.httpClient.get('/contacts', {headers: headers})
.pipe(timeout(this.authService.getTimeoutLimit('normal')));
COMPONENT.TS
this.httpService.yourObservableName$.subscribe(...)
Upvotes: 0
Reputation: 42596
Short answer, yes, you still unsubscribe to your observables in your component to avoid subscription leaks. One of my preferred ways of doing so would be to make use of the takeUntil() operator.
This is how you can use it in your component.
private unsubscribe: Subject<void> = new Subject();
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
getContacts() {
this.httpService.getContactsHttp()
.pipe(
takeUntil(this.unsubscribe),
).subscribe((data:ContactSearch[])=>{
this.contacts$.next(data);
});
}
As explained by Brian Love,
- First, we import the takeUntil() operator as well as the Subject class.
- Next, we define a private instance property named unsubscribe, which is a Subject.
- We also create a new instance of Subject, defining the generic type as void. We use the takeUntil() operator in the pipe() method before invoking subscribe(), providing the unsubscribe observable.
- In the ngOnDestroy() lifecycle method we emit a next() notification, and then complete() the unsubscribe observable. The subscription is now complete, and we have immediately unsubscribed when the ngOnDestroy() method is invoked during the lifecycle of our component.
Upvotes: 4
Reputation: 79
Yes, its a good practice to unsubscribe all the subscriptions in ngOnDestroy lifecycle method, you can do it by taking a reference of each subscription in class level variable and then unsubscribe them manually!
Upvotes: -1