Report this

What is the reason for this report?

How To Use the takeUntil RxJS Operator to Manage Subscriptions Declaratively

Updated on March 23, 2021
How To Use the takeUntil RxJS Operator to Manage Subscriptions Declaratively

Introduction

Angular handles unsubscribing from observable subscriptions like those returned from the HTTP service or when using the async pipe. However, for other situations, it can quickly become difficult to manage all subscriptions and ensure to unsubscribe from those that are long-lived. A policy of unsubscribing from most subscriptions will also have its own problems.

In this article, you will be presented with an example Angular application that relies upon manually subscribing and unsubscribing. Then, you will compare it to an example Angular application that uses the takeUntil operator to declaratively manage subscriptions.

Prerequisites

If you would like to follow along with this article, you will need:

  • Some familiarity with the RxJS library, in particular, Observable and Subscription will be beneficial.
  • Some familiarity with Apollo and GraphQL will be helpful but is not required.

This tutorial was verified with Node v15.3.0, npm v6.14.9, @angular/core v11.0.4, rxjs v6.6.3, apollo-angular v2.1.0, graph-tag v2.11.0. This article was edited to reflect changes in migrating from earlier versions of @angular/core and rxjs.

Unsubscribing Manually

Let’s start with an example where you will manually unsubscribe from two subscriptions.

In this example, the code is subscribing to an Apollo watchQuery to get data from a GraphQL endpoint.

The code is also creating an interval observable that you subscribe to when an onStartInterval method gets called.

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

import { Subscription, interval } from 'rxjs';

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  myQuerySubscription: Subscription;
  myIntervalSubscription: Subscription;

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.myQuerySubscription = this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    this.myIntervalSubscription = interval(250).subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.myQuerySubscription.unsubscribe();

    if (this.myIntervalSubscription) {
      this.myIntervalSubscription.unsubscribe();
    }
  }
}

Now imagine that your component has many similar subscriptions, it can quickly become quite a process to ensure everything gets unsubscribed when the component is destroyed.

Unsubscribing Declaratively with takeUntil

The solution is to compose the subscriptions with the takeUntil operator and use a subject that emits a truthy value in the ngOnDestroy lifecycle hook.

The following snippet does the exact same thing, but this time the code will unsubscribe declaratively. You will notice that an added benefit is that you no longer need to keep references to our subscriptions anymore.

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

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

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  destroy$: Subject<boolean> = new Subject<boolean>();

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .pipe(takeUntil(this.destroy$))
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    interval(250)
    .pipe(takeUntil(this.destroy$))
    .subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    this.destroy$.unsubscribe();
  }
}

Notice how using an operator like takeUntil instead of manually unsubscribing will also complete the observable, triggering any completion event on the observable.

Be sure to check your code to make sure this does not create any unintended side effects.

Conclusion

In this article, you learned about using takeUntil to declaratively unsubscribe. Unsubscribing from unnecessary subscriptions contributes towards preventing memory leaks. Declaratively unsubscribing allows you to not require references to subscriptions.

There are other similar RxJS operators - like take, takeWhile, and first - which will all complete the observable.

If you’d like to learn more about Angular, check out our Angular topic page for exercises and programming projects.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author

Alligator
Alligator
Author
See author profile

Alligator.io is a developer-focused resource that offers tutorials and insights on a wide range of modern front-end technologies, including Angular 2+, Vue.js, React, TypeScript, Ionic, and JavaScript.

Category:
Tags:

Still looking for an answer?

Was this helpful?


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Do you still need this.destroy$.unsubscribe(); ?

This comment has been deleted

This comment has been deleted

This comment has been deleted

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.
Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.