Mastering when & how to unsubscribe from the observables with RxJs

Abhinav Akhil
6 min readFeb 12, 2024

--

Dear readers, Before understanding when to unsubscribe from observables, Let me briefly introduce what exactly is Rxjs and an Observable and its types to understand the concept in depth.

What is Rxjs?

In simplest term, Rxjs is a library used to observe & react to data as it flows through time & it works with asynchronous requests and responses.

What is Observable?

Observable is a collection of events or values emitted over time ex: user actions, application events ( forms, routing ), response from HTTP request etc.

Observable can be asynchronous ( infinite emission) or synchronous ( finite emission, for example: 1, 2, 3 ).

Also, Observables can emit three types of notifications: next, which delivers a new value; error, which signals an error; and complete, which indicates the end of the observable stream.

Here is an example of observable:

Until we subscribe to the observable they don't emit anything, though few observables work without subscription ( ex subject ).

Types of Observables?

RxJS provides several types of observables to cater to different use cases and scenarios. The main types of observables in RxJS include:

Cold Observables: Cold observables start emitting values only when a subscription is made. Each subscription triggers the execution of the observable’s logic independently. Subscribers receive the entire sequence of values from the beginning, and multiple subscribers can receive the same set of values.

Hot Observables: Hot observables, on the other hand, emit values regardless of whether there are subscribers. Subscribers receive values emitted after they subscribe, and they share the same set of values. Hot observables are often used for scenarios where you want subscribers to receive the same set of values, such as with event streams.

Subscribing to an observable?

Subscribing to an observable is the process by which you listen for and react to the values emitted by the observable. In RxJS, you use the subscribe method to establish a connection between an observable and an observer.

The subscribe method takes up to three arguments: a function to handle emitted values (next), a function to handle errors (error), and a function to handle the completion of the observable (complete).

Here’s a basic example of how to subscribe to an observable:

// Import the Observable class from RxJS
import { Observable } from 'rxjs';

// Create a simple observable
const simpleObservable = new Observable(observer => {
observer.next('Hello');
observer.next('World');
observer.complete();
});

// Subscribe to the observable
const subscription = simpleObservable.subscribe(
value => console.log(value), // next handler
error => console.error(error), // error handler
() => console.log('Completed') // complete handler
);
  • The next handler logs each emitted value to the console.
  • The error handler logs any errors that may occur during the observable's execution.
  • The complete handler is called when the observable completes, indicating that it won't emit any more values.

it’s important to note that the subscribe method returns a subscription object. This subscription object can be used to unsubscribe from the observable, preventing further emissions and freeing up resources when they are no longer needed. Here's an example of unsubscribing after a certain period:

// Unsubscribe after a certain period (e.g., 2 seconds)
setTimeout(() => {
subscription.unsubscribe();
}, 2000);

by calling unsubscribe(), you terminate the subscription and stop listening to the observable.

why unsubscribe an observable?

Unsubscribing from an observable is a crucial practice in reactive programming, and it serves several important purposes:

Memory Management: When you subscribe to an observable, a reference to the subscriber is created. If you don’t unsubscribe when you no longer need the observable (e.g., when a component is destroyed), these references can accumulate over time, leading to memory leaks. Unsubscribing ensures that these references are released, preventing memory leaks and improving the overall performance and stability of your application.

Resource Cleanup: Observables often involve asynchronous operations, such as HTTP requests or timers. Unsubscribing allows you to clean up resources associated with these operations. For example, it helps in canceling ongoing HTTP requests, stopping timers, or releasing other resources that might be held by the observable.

Avoiding Unexpected Behavior: Lingering subscriptions can lead to unexpected behavior in your application. For instance, a component might receive and process events or data even after it’s been destroyed, causing errors or displaying outdated information. Unsubscribing ensures that no further processing occurs once the subscriber is no longer active.

Different ways to unsubscribe from an observable:

There are several ways to unsubscribe from an observable in RxJS, providing flexibility based on your application’s requirements and the context in which the observable is used. Here are some common approaches:

Manual Unsubscription: Save the subscription when you subscribe to the observable and manually call the unsubscribe method when you no longer need the subscription. This is a straightforward approach but requires careful management, especially in scenarios where you have multiple subscriptions.

const observable = // your observable creation logic;

// Subscribe and save the subscription
const subscription = observable.subscribe(
value => console.log(value),
error => console.error(error),
() => console.log('Completed')
);

// Unsubscribe when no longer needed
subscription.unsubscribe();

Using Operators like take or takeUntil: RxJS provides operators like take or takeUntil that allow you to automatically unsubscribe after a certain number of emissions or when a specific condition is met. This can be useful for scenarios where you want to limit the number of emissions or unsubscribe based on some external trigger.

const observable = // your observable creation logic;

// Unsubscribe after the first emission
observable.pipe(take(1)).subscribe(
value => console.log(value),
error => console.error(error),
() => console.log('Completed')
);

Leveraging Lifecycle Hooks in Angular: In Angular, you can use lifecycle hooks like ngOnDestroy to automatically unsubscribe when a component is destroyed. This helps prevent memory leaks associated with lingering subscriptions.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
selector: 'app-example',
template: '<p>{{ data }}</p>',
})
export class ExampleComponent implements OnInit, OnDestroy {
data: any;
private subscription: any;

ngOnInit() {
const observable = // your observable creation logic;

// Subscribe and save the subscription
this.subscription = observable.subscribe(
value => this.data = value,
error => console.error(error),
() => console.log('Completed')
);
}

ngOnDestroy() {
// Automatically unsubscribe when the component is destroyed
this.subscription.unsubscribe();
}
}

Using the async Pipe in Angular: In Angular, you can use the async pipe to subscribe to observables directly in the template. Angular automatically manages the subscription and unsubscribes when the component is destroyed.

import { Component } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
selector: 'app-example',
template: '<p>{{ observable$ | async }}</p>',
})
export class ExampleComponent {
observable$: Observable<any> = // your observable creation logic;
}

Choose the method that best fits your use case and application architecture. While manual unsubscription gives you more control, using lifecycle hooks or the async pipe can simplify the management of subscriptions in certain scenarios.

In summary, our exploration into Observables has equipped us with a deeper understanding of their functionality. We’ve delved into the distinctions between hot and cold observables, recognizing the scenarios that necessitate unsubscribing from them for optimal application performance. A key takeaway from our journey is the significance of leveraging the `async` pipe whenever feasible, streamlining the process of handling subscriptions and contributing to cleaner, more efficient code.

--

--

Abhinav Akhil
Abhinav Akhil

Written by Abhinav Akhil

I create software, that tell stories.

No responses yet