When Observable is used in Angular Application it is sometimes necessary to unsubscribe from Observable.
Observables are classified as finite and infinite.
Infinite observables cause memory leaks which will lead to performance issues. It will cause the slowness in loading of the page and even sometimes it might cause the server to hang.
First, you need a fair knowledge of what is Observable and Observable Operators.
Finite Observables
HTTP call
routing
Infinite Observables
Event Listener
timer
interval
How to find Infinite Observables?
Keep a console.log or debug at the breakpoint. If it continuously logs or loops it will be an Infinite Observable otherwise it is finite.
Finite observables need not be unsubscribed. An HTTP request is finite because when a request response is completed it is done. There will be a single request and a single response. So you don't need to unsubscribe from an HTTP observable.
If you use an async pipe then it will automatically subscribe when observable emits and unsubscribes when observable completes.
When to Unsubscribe?
- When there is a Subscription to an Observable.
- For HTTP service is not needed, but if you use evenListener on the HTTP request, then Unsubscribe it.
- When there are event listeners added to DOM events.
- Infinite Observables added to operators like timer, interval, etc.
- When there is a Subscription to valueChanges.
Practically it is best to unsubscribe from any observables that you have subscribed to.
How to Unsubscribe?
Unsubscribe from an Observable Subscription is done by calling the unsubscribe() method in the ngOnDestroy lifecycle hook method.
Component with an HTTP service call
This call has a subscribe method and does not have any unsubscribe method.
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
url: string = 'http://jsonplaceholder.typicode.com/posts/1';
response:any = {};
private post1: Subscription;
constructor(private http: HttpClient) { }
ngOnInit() {
this.http.get(this.url).subscribe(
data => {
this.response = data;
console.log(data);
}, error => {
console.log(error);
}
);
}
}
Component with unsubscribe method
Here we assign the HTTP's subscribe call to the Subscription. Subscription has one method unsubscribe.
unsubscribe() doesn't take any parameters.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
url: string = 'http://jsonplaceholder.typicode.com/posts/1';
response:any = {};
private post1: Subscription;
constructor(private http: HttpClient) { }
ngOnInit() {
this.post1 = this.http.get(this.url).subscribe(
data => {
this.response = data;
console.log(data)
}, error => {
console.log(error);
}
);
}
ngOnDestroy(){
this.post1.unsubscribe();
}
}
Component with more unsubscribe methods
In this call, if you see for each service call you need to have separate unsubscribe methods. You have to unsubscribe each service calls separately.
If there are ten service calls, then there will be ten unsubscribe methods. To avoid this, we can declare a common unsubscribe class and reuse it across the application.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
post1_url: string = 'http://jsonplaceholder.typicode.com/posts/1';
post2_url: string = 'http://jsonplaceholder.typicode.com/posts/2';
response:any = {};
response2:any = {};
private post1 : Subscription;
private post2 : Subscription;
constructor(private http: HttpClient) { }
ngOnInit() {
this.post1 = this.http.get(this.post1_url).subscribe(
data => {
this.response = data;
console.log(data)
}, error => {
console.log(error);
}
);
this.post2 = this.http.get(this.post2_url).subscribe(
data => {
this.response2 = data;
console.log(data)
}, error => {
console.log(error);
}
);
}
ngOnDestroy(){
this.post1.unsubscribe();
this.post2.unsubscribe();
}
}
Best Way to Unsubscribe from Observable
To resolve the above issue Use RxJS takeUntil operator. For this, I have created a common component with ngOnDestroy lifecycle hook method.
Use command to generate the UnsubsribeComponent
ng g c Unsubsribe
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
@Component({
template: ``
})
export class UnsubsribeComponent implements OnDestroy {
destroyed$ = new Subject<void>();
ngOnDestroy() {
this.destroyed$.next();
}
}
Now import and extend the UnsubcribeComponent in your component.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject, Subscription } from 'rxjs';
import { takeUntil } from "rxjs/operators";
import { UnsubsribeComponent } from './unsubsribe/unsubsribe.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent extends UnsubsribeComponent implements OnInit {
post1_url: string = 'http://jsonplaceholder.typicode.com/posts/1';
post2_url: string = 'http://jsonplaceholder.typicode.com/posts/2';
response:any = {};
response2:any = {};
private post1 : Subscription;
private post2 : Subscription;
constructor(private http: HttpClient) {
super();
}
ngOnInit() {
this.post1 = this.http.get(this.post1_url).pipe(takeUntil(this.destroyed$)).subscribe(
data => {
this.response = data;
console.log(data)
}, error => {
console.log(error);
}
);
this.post2 = this.http.get(this.post2_url).pipe(takeUntil(this.destroyed$)).subscribe(
data => {
this.response2 = data;
console.log(data)
}, error => {
console.log(error);
}
);
}
}