Unsubscribe from RxJS Observables to avoid memory leak
Wednesday, October 23, 2019
Unsubscribe from RxJS Observables to avoid memory leak
RxJS is the incredible powerful reactive extensions for JavaScript library, but as Peter Parker's uncle Ben says: "With great power comes great responsibility".
Our responsibility when subscribing to an observable stream is to be sure that we unsubscribe. We unsubscribe when we not longer need additional (next, complete or error) notifications. In a component based framework such as React or Angular it is most common to unsubscribe from a subscription when the component is unmounted or destroyed. Both React and Angular provide a mechanism, or life-cycle method, for cleaning things up when a component is unmounted or destroyed.
If we fail to unsubscribe we could have a possible memory leak in our application. The memory leak is caused by unnecessary subscriptions.
It is important to understand that the signature for the subscribe()
method in the Observable
class is:
subscribe(observerOrNext?: PartialObserver<T> | ((value: T) => void), error?: (error: any) => void, complete?: () => void): Subscription
There is a lot of type declarations in the above signature. For now, let's focus on the return type: Subscription
. This informs us that when we invoke the subscribe()
method that we receive a new Subscription
instance.
The Subscription
class enables us to manage a subscription. Let's have a quick look at the Subscription
class:
class Subscription implements SubscriptionLike {
static EMPTY: Subscription
constructor(unsubscribe?: () => void)
closed: [object Object]
unsubscribe(): void
add(teardown: TeardownLogic): Subscription
remove(subscription: Subscription): void
}
Take note of the following methods:
unsubscribe()
will unsubscribe this subscription, along with all child subscriptionsadd()
will add a child subscription to this subscription
Using these methods we can unsubscribe from RxJS observables to avoid a memory leak.
Instructions
Use Subscription.add()
to add child subscriptions
Invoke Subscription.unsubscribe()
, often when unmounting or destroying a component
The takeUntil()
operator approach.
Similar to event listeners, open subscriptions that are unecessary leads to memory leaks in your application
Code Examples
Foo
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material';
import { Rule } from '@lkt-core/models';
import { ConfirmationDialogComponent, ConfirmationDialogData } from '@lkt-shared/components';
import { RuleFacade } from '@lkt-state/rule';
import { Observable } from 'rxjs';
@Component({
templateUrl: './index.component.html',
styleUrls: ['./index.component.scss']
})
export class IndexComponent implements OnInit {
/** Toggle loading indicator. */
loading: Observable<boolean>;
/** The rules to display. */
rules: Observable<Array<Rule>>;
constructor(private readonly matDialog: MatDialog, private readonly ruleFacade: RuleFacade) {}
ngOnInit() {
this.loading = this.ruleFacade.loading;
this.rules = this.ruleFacade.loadAll();
}
onRemove(rule: Rule): void {
const data: ConfirmationDialogData = { title: 'Remove Rule' };
const dialogRef = this.matDialog.open(ConfirmationDialogComponent, {
data
});
dialogRef.afterClosed().subscribe(value => value && this.ruleFacade.remove(rule));
}
}
Have a question or comment?