Skip to content

Managing Subscriptions (unsubscribe, async pipe, takeUntil)

Memory leaks are a common pitfall in Angular when using Observables. Subscriptions that are not cleaned up can cause performance degradation and unexpected behavior. Here’s how to manage them properly.

Store the Subscription and call unsubscribe() in ngOnDestroy.

import { Component, OnInit, OnDestroy } from "@angular/core";
import { interval, Subscription } from "rxjs";
@Component({
selector: "app-counter",
template: "<div>{{count}}</div>",
})
export class CounterComponent implements OnInit, OnDestroy {
count = 0;
private subscription: Subscription;
ngOnInit() {
this.subscription = interval(1000).subscribe((value) => {
this.count = value;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}

The async pipe subscribes automatically and unsubscribes when the component is destroyed. It’s the preferred way to handle Observables in templates.

import { Component } from "@angular/core";
import { interval } from "rxjs";
import { map } from "rxjs/operators";
@Component({
selector: "app-counter",
template: "<div>{{counter$ | async}}</div>",
})
export class CounterComponent {
counter$ = interval(1000).pipe(map((value) => value));
}

A robust pattern for unsubscribing from multiple Observables using a single notifier.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { interval, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({...})
export class CounterComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
ngOnInit() {
interval(1000).pipe(
takeUntil(this.destroy$)
).subscribe(value => console.log(value));
// Another observable
interval(2000).pipe(
takeUntil(this.destroy$)
).subscribe(value => console.log('Another:', value));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

The destroy$ Subject emits in ngOnDestroy, causing all takeUntil observables to complete and clean up.

  • take(n): automatically completes after n emissions.
  • first() / take(1): takes the first value then completes.
  • takeWhile(predicate): takes values while condition holds.

Example:

import { interval } from "rxjs";
import { take } from "rxjs/operators";
interval(1000).pipe(take(5)).subscribe(console.log); // emits 0,1,2,3,4 then completes
MethodBest for
Manual unsubscribeWhen you need fine control or have a single subscription
async pipeTemplate bindings – simplest, most reliable
takeUntilMultiple subscriptions in a component
take / firstFinite streams where auto‑completion makes sense

Always clean up subscriptions to prevent memory leaks in long‑lived applications.