0

I started working with a legacy angular 6 application that is using Rxjs observables. The observables within the components don't seem to be using an unsubscribe from what I can see. Example subscribe within the ngOnInit:

this.service.getCustomerDetail(customers).subscribe(
          (data) => {
            this.customerData = data;
          },
          () => {
            this.notify.addNotification(
              new notification(
                `Error retrieving customer data`
              )
            );
          }
        );

The subscribes are using the completion even if nothing in it aka Next, Error, Complete values of Rxjs subscribes but this to me looks like it needs to push these into subscriptions and then on the ngOnDestroy unsubscribe to all of them. Is that correct that these subscriptions without that unsubscribe even if the complete is within the observable would stay in the heap memory aka cause memory leaks?

Using the TakeUntil approach would I use the TakeUntil in the cases where my subscribe is using the error and complete sections for example:

this.service.getCustomerDetail(customers)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(
          (data) => {
            this.customerData = data;
          },
          () => {
            //error section
            this.notify.addNotification(
              new notification(
                `Error retrieving customer data`
              )
            );
          },
          () => {
            //complete section
            this.loadAddlDetail();
          }
        );

Thanks for any info on this.

beantownace
  • 117
  • 1
  • 10
  • The takeUntil method is the preferred angular solution https://stackoverflow.com/questions/38008334/angular-rxjs-when-should-i-unsubscribe-from-subscription?rq=1 – Adrian Brand Jul 08 '20 at 04:30
  • @AdrianBrand added a question to the description with example would I use takeUntil if I already have a error and complete section to the subscribe? – beantownace Jul 08 '20 at 17:14

2 Answers2

2

There are 2 ways to do this. If this is an isolated case the following will suffice

Method 1 - If this is an isolated case

---------------------------------------------------

In class

Change the class Declaration to include the OnDestroy

export class AppComponent implements OnInit, OnDestroy

Add private variable to store the subscription

private serviceSubscription  = null

In ngOnInit

this.serviceSubscription = this.service.getCustomerDetail(customers).subscribe(
      (data) => {
        this.customerData = data;
      },
      () => {
        this.notify.addNotification(
          new notification(
            `Error retrieving customer data`
          )
        );
      }
    );

Implement OnDestroy

ngOnDestroy() {
   if (this.serviceSubscription != null) {
     this.serviceSubscription.unsubscribe()
   }
} 

----------------------------------------------------------

Method 2 - If this seen in multiple places

----------------------------------------------------------

Create a Base Class for Components component-base.ts

import { Subscription } from 'rxjs';
import { OnDestroy } from '@angular/core';

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

    protected rxs(s: Subscription): void {
        this.subscriptions.push(s);
    }

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

Extend the component from the ComponentBase

export class AppComponent extends ComponentBase implements OnInit

In ngOnInit

ngOnInit() {
    this.rxs(this.service.getCustomerDetail(customers).subscribe(
        (data) => {
        this.customerData = data;
        },
        () => {
        this.notify.addNotification(
            new notification(
            `Error retrieving customer data`
            )
        );
        }
    ));
}
Supun De Silva
  • 1,437
  • 9
  • 15
  • awesome thanks I appreciate the feedback and examples. – beantownace Jul 08 '20 at 03:10
  • 2
    The Angular standard way is to use takeUntil with a single subject to complete all observables or use the async pipe. Arrays of subscriptions is not a good solution. – Adrian Brand Jul 08 '20 at 04:31
  • Thanks Adrian I am looking into that – beantownace Jul 08 '20 at 12:16
  • @AdrianBrand what if my subscribe already has a complete on the 3rd section aka I have a next where it gets the data, error then a complete section that calls a few other methods. In that case should I use the TakeUntil that will call .next and .complete on the ngOnDestroy? – beantownace Jul 08 '20 at 17:02
0

you can use ngOnDestroy() and add the unsubscribe() in it like below

ngOnDestroy() {
 if(serviceSubscription != null){
  serviceSubscription.unsubscribe();
 } 
}

or you can use async pipe on the html template itself, so that you don't have to unsubscribe it manually like below.

<p>{{ observable | async }}</p>
Pradeep
  • 1,192
  • 2
  • 12
  • 30
  • if your issue resolved, please accept the answer. Thank you – Pradeep Jul 08 '20 at 04:15
  • So the subscribe is hitting an http get for example and it loads that data into an object array which is persisted and used. What happens in the case when the unsubscribe is not added is it the callback that stays as allocated memory on the heap until it is destroyed? – beantownace Jul 08 '20 at 12:10
  • I am looking at using the TakeUntil as well but doesn't the service http call when it comes back in the Next I set that into an object array then I have the error and complete areas of the subscribe. Does the complete not automatically get called if no error that is where the confusion is. If I add for example the TakeUntil on the ngOnDestroy it calls the complete and next as well. – beantownace Jul 08 '20 at 12:21