Angular Subscribing And Unsubscribing Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    7 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of Angular Subscribing and Unsubscribing

Angular Subscribing and Unsubscribing

Understanding Observables and Subscriptions

Observables are used extensively in Angular for a variety of tasks including API responses, event handling, timers, and more. They provide a mechanism for pushing changes to a set of subscribers over time and can be thought of as collections that emit multiple async values.

A subscription is an object that represents the execution of an observable. Subscriptions allow you to start and stop the flow of data from an observable. When subscribing to an observable, you specify how the emitted value should be handled by providing a callback function, often using the subscribe() method.

import { Observable, of } from 'rxjs';

const myObservable = of(1, 2, 3, 4, 5).pipe(
  // operators can be added here
);

myObservable.subscribe({
  next: (x) => console.log(x),
  error: (e) => console.error(e),
  complete: () => console.log('done'),
});

In this example, every emission from myObservable triggers the next function, logging the value. If there's an error or the observable completes, respective callbacks are triggered.

Common Scenarios Requiring Subscription Management

  • HTTP Requests: When fetching data from an API, it's important to unsubscribe when the component is destroyed to ensure no hanging requests.
  • Component Communication: Using observables for communication between components.
  • Routing Guards: In scenarios where guarding routes involves subscriptions.
  • Shared Services: Subscribing to services shared across multiple components, which may lead to unintended data flow if not managed properly.

Why Memory Leaks Happen?

Memory leaks occur when an application retains references to objects that are no longer needed. For observables in Angular, memory leaks can happen if you subscribe to an observable (such as an HTTP request) but forget to unsubscribe when the component that created the subscription is destroyed. This keeps the component in memory even after it's no longer visible, continuing to receive data updates and causing unnecessary resource consumption.

To demonstrate the importance of unsubscribing:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { DataService } from './data.service';

@Component({
  selector: 'app-my-component',
  template: `<div>{{ data }}</div>`,
})
export class MyComponent implements OnInit, OnDestroy {
  data: string;
  subscription: Subscription;

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    this.subscription = this.dataService.getData().subscribe(
      (d) => (this.data = d)
    );
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

Here, MyComponent subscribes to an observable provided by DataService. It stores the subscription in a variable and calls .unsubscribe() within the lifecycle hook ngOnDestroy.

Strategies for Automatic Unsubscription

Using manual subscription management as shown above can become cumbersome, especially in larger applications. Hence, several strategies exist for automatically handling unsubscriptions:

  1. Subscription Add Method: You can use one Subscription to hold multiple subscriptions and then call unsubscribe() on that single subscription.

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { Subscription } from 'rxjs';
    import { DataService } from './data.service';
    
    @Component({
      selector: 'app-my-component',
      template: `<div>{{ data }}</div>`,
    })
    export class MyComponent implements OnInit, OnDestroy {
      data: string;
      private allSubscriptions = new Subscription();
    
      constructor(private dataService: DataService) {}
    
      ngOnInit(): void {
        this.allSubscriptions.add(
          this.dataService.getData().subscribe((d) => (this.data = d))
        );
    
        this.allSubscriptions.add(
          this.dataService.getMoreData().subscribe((g) => {
            // handle more data
          })
        );
      }
    
      ngOnDestroy(): void {
        this.allSubscriptions.unsubscribe();
      }
    }
    
  2. TakeUntil Operator: The takeUntil operator helps you complete your observable when another observable emits a value. Typically, you would use NgRx Store actions, or a Subject that emits when the component is destroyed.

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { Subject } from 'rxjs';
    import { takeUntil } from 'rxjs/operators';
    import { DataService } from './data.service';
    
    @Component({
      selector: 'app-my-component',
      template: `<div>{{ data }}</div>`,
    })
    export class MyComponent implements OnInit, OnDestroy {
      data: string;
      private destroy$ = new Subject<void>();
    
      constructor(private dataService: DataService) {}
    
      ngOnInit(): void {
        this.dataService.getData()
          .pipe(takeUntil(this.destroy$))
          .subscribe((d) => (this.data = d));
      }
    
      ngOnDestroy(): void {
        this.destroy$.next();
        this.destroy$.complete();
      }
    }
    
  3. Async Pipe: The async pipe automatically unsubscribes when the current view changes. While not ideal for all use cases (especially where you need to perform multiple operations or complex transformations), it is very convenient for simple scenarios.

    import { Component } from '@angular/core';
    import { DataService } from './data.service';
    
    @Component({
      selector: 'app-my-component',
      template: `<div>{{ data$ | async }}</div>`, // automatically unsubscribes
    })
    export class MyComponent {
      data$ = this.dataService.getData();
    
      constructor(private dataService: DataService) {}
    }
    

Conclusion

Managing subscriptions effectively is vital for optimal performance and avoiding memory leaks in Angular applications. By understanding the importance of subscription cleanup and utilizing best practices such as takeUntil, manual management with Subscription, or leveraging the async pipe, developers can maintain clean and efficient reactive code.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Angular Subscribing and Unsubscribing

Step-by-Step Guide: Subscribing and Unsubscribing in Angular

Step 1: Setting Up the Environment

First, make sure you have Angular CLI installed globally. If not, you can install it using npm:

npm install -g @angular/cli

Create a new Angular project:

ng new observable-app
cd observable-app

Step 2: Creating a Service

Generate a new service that will emit some data using an observable.

ng generate service data

Edit src/app/data.service.ts to return an observable:

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

@Injectable({
  providedIn: 'root'
})
export class DataService {
  // Creating an Observable that emits a number every second
  getData(): Observable<number> {
    return new Observable((observer) => {
      let count = 0;
      const intervalId = setInterval(() => {
        observer.next(count++);
      }, 1000);

      return {
        unsubscribe() {
          clearInterval(intervalId);
        }
      };
    });
  }
}

Step 3: Creating a Component

Generate a component that will subscribe to the observable and display the data:

ng generate component counter

Edit src/app/counter/counter.component.ts:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { DataService } from '../data.service';

@Component({
  selector: 'app-counter',
  template: `
    <h2>Counter: {{ counterValue }}</h2>
  `,
  styles: [``]
})
export class CounterComponent implements OnInit, OnDestroy {
  counterValue: number;
  subscription: Subscription;

  constructor(private dataService: DataService) {}

  ngOnInit(): void {
    // Subscribing to the observable from DataService
    this.subscription = this.dataService.getData().subscribe((value) => {
      this.counterValue = value;
    });
  }

  ngOnDestroy(): void {
    // Unsubscribing from the observable to prevent memory leaks
    this.subscription.unsubscribe();
  }
}

Step 4: Updating the App Module

Ensure that the CounterComponent is declared in the AppModule:

Edit src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { CounterComponent } from './counter/counter.component';

@NgModule({
  declarations: [
    AppComponent,
    CounterComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 5: Using the Counter Component

Edit src/app/app.component.html to include the CounterComponent:

<app-counter></app-counter>

Step 6: Running the Application

Run the application using Angular CLI:

ng serve

Navigate to http://localhost:4200 in your web browser. You should see the counter value updating every second.

Step 7: Understanding Subscription Management

  • Subscription: An object with an unsubscribe() method that cleans up the resource or data source.
  • ngOnInit: This lifecycle hook is called when the component is initialized. Here, we subscribe to the DataService observable.
  • ngOnDestroy: This lifecycle hook is called when the component is destroyed. We use it to unsubscribe from the observable to prevent memory leaks.

Conclusion

In Angular, managing subscriptions properly is crucial to avoid memory leaks. By following these steps, you can successfully subscribe to and unsubscribe from observables. This pattern ensures that your application remains performant and efficient.

If you want to handle multiple subscriptions in a component, you can use Subscription chaining:

Top 10 Interview Questions & Answers on Angular Subscribing and Unsubscribing

Top 10 Questions and Answers on Angular Subscribing and Unsubscribing

1. What is Subscribing in Angular?

Answer: In Angular, subscribing involves listening to an Observable to receive asynchronous data or notifications. An Observable is a core concept of Reactive Extensions (RxJS), which Angular uses for handling asynchronous operations such as HTTP requests, events, or streams of data. When you subscribe to an Observable, you provide a callback function that will be executed when the Observable emits a value.

2. Why do we need to Unsubscribe in Angular?

Answer: Unsubscribing is crucial because it prevents memory leaks by removing listeners that are no longer needed. When an Observable is subscribed to, Angular creates a subscription that will continue to receive updates until it is unsubscribed. If a component subscribes to an Observable but doesn’t unsubscribe when the component is destroyed or the subscription is no longer needed, the subscription will remain active, causing memory leaks and potentially leading to performance issues.

3. How do you Unsubscribe in Angular?

Answer: You can unsubscribe in Angular by calling the unsubscribe() method on a Subscription object. Typically, you store the subscription in a variable and call unsubscribe() in the ngOnDestroy lifecycle hook of the component. For example:

export class MyComponent implements OnDestroy {
  private subscription: Subscription;

  constructor(private myService: MyService) {
    this.subscription = this.myService.getObservable().subscribe(data => {
      // handle data
    });
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}

4. What is the advantage of using a Subscription Store?

Answer: Using a Subscription store (or an array to hold all subscriptions) can be useful when a component has multiple subscriptions. By storing all subscriptions in an array, you can easily unsubscribe from all of them in the ngOnDestroy method with a single line of code:

export class MyComponent implements OnDestroy {
  private subscriptions: Subscription[] = [];

  constructor(private myService: MyService) {
    this.subscriptions.push(this.myService.getObservable().subscribe(data => {
      // handle data
    }));
  }

  ngOnDestroy() {
    this.subscriptions.forEach(sub => sub.unsubscribe());
  }
}

5. Can you use takeUntil for Automatic Unsubscription?

Answer: Yes, takeUntil is an operator provided by RxJS that allows you to automatically unsubscribe from an Observable when another Observable emits a value, typically a component's ngOnDestroy event. This can make your code cleaner:

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class MyComponent implements OnDestroy {
  private destroy$ = new Subject<void>();

  constructor(private myService: MyService) {
    this.myService.getObservable().pipe(
      takeUntil(this.destroy$)
    ).subscribe(data => {
      // handle data
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

6. Why should you Unsubscribe from HttpRequests?

Answer: While Angular's HttpClient automatically unsubscribes from HTTP requests when the component is destroyed, it's still a good practice to manage subscriptions explicitly, especially in larger applications where components might be dynamically created and destroyed. Unsubscribing ensures that any ongoing HTTP requests are terminated when they are no longer needed, reducing the risk of memory leaks.

7. How do you Unsubscribe from RxJS Subjects?

Answer: RxJS Subjects are both Observable and Observer, and they can be subscribed to just like any other Observable. To unsubscribe from a Subject, you also use the unsubscribe() method on the Subscription object:

export class MyComponent implements OnDestroy {
  private subscription: Subscription;

  constructor(private mySubject: Subject<any>) {
    this.subscription = this.mySubject.subscribe(data => {
      // handle data
    });
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}

8. What happens if you forget to Unsubscribe?

Answer: Forgetting to unsubscribe can lead to memory leaks, especially in components that create multiple subscriptions or that persist across multiple navigation states. Memory leaks occur because the Observable continues to emit values and maintain a reference to the callback function, preventing the garbage collector from releasing the memory used by the component and its subscriptions.

9. Is there any way to automatically Unsubscribe in Angular without manually calling unsubscribe()?

Answer: Yes, there are tools and libraries that can help manage subscriptions automatically. One such tool is ngneat/until-destroy, which is a decorator that simplifies unsubscribing by automatically calling unsubscribe() on all subscriptions when the component is destroyed:

import { untilDestroyed } from '@ngneat/until-destroy';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
})
@untilDestroyed() // DECORATOR
export class MyComponent implements OnDestroy {
  constructor(private myService: MyService) {
    this.myService.getObservable().pipe(
      untilDestroyed(this) // HELPER FUNCTION
    ).subscribe(data => {
      // handle data
    });
  }

  ngOnDestroy() {}
}

10. When should you NOT unsubscribe?

Answer: In some situations, you might not need to unsubscribe:

  • Hot Observables: These are shared Observables where data is emitted regardless of whether there are subscribers or not (e.g., Subject, BehaviorSubject, ReplaySubject). If a Subject continues emitting values after the component is destroyed, it may be intentional.
  • Static or Long-Lived Observables: If the Observable is static, long-lived, and does not retain references to components, you might not need to unsubscribe. However, it’s generally safer to manage subscriptions explicitly.

You May Like This Related .NET Topic

Login to post a comment.