2
votes

I have an angular component where I request values from a service periodically:

setInterval(() => {
      this.updateValues();
    }, 1000);


updateValues(): void {
  if (this.isValuesUpdateRunning === false) {
    this.isValuesUpdateRunning = true;
    this.apiService.getCurrentValues().subscribe((data: any) => {
      if (data != null) {
        this.currentValues = data;
        this.isValuesUpdateRunning = false;
      }
    });
  }
}

I'm pretty sure that I should unsubscribe from the service somewhere because I do open a lot of subscriptions by doing this. I was thinking about using async pipe, but I need to set the "isValueUpdateRunning" variable and that wouldn't be possible if I did just use async within the template.

Also, unsubscribing within the "onDestroy" method provided by Angular doesn't help either because as far as I understand, this method only gets called when I leave the page. But I need to close my subscriptions more often to prevent keeping thousands of subscriptions open.

I also tried closing the subscription immediately after setting my values, but then I'm not getting any values within my view:

updateValues(): void {
  if (this.isValuesUpdateRunning === false) {
    this.isValuesUpdateRunning = true;
    const sub = this.apiService.getCurrentValues().subscribe((data: any) => {
      if (data != null) {
        this.currentValues = data;
        this.isValuesUpdateRunning = false;
      }
    });
    // closing the subscription right here:
    sub.unsubscribe();
  }
}

Can you help please? I'm running out of ideas.

3
If this.apiService.getCurrentValues() is making an http request with the Angular HttpClient, you don't need to unsubscribe. - Kurt Hamilton
You are not getting values because you are unscubscibing before getting an answer. And i agree wit the above comment about http requests. - David Gonzalez
@KurtHamilton I'm using the HttpClient, yes. It's just a simple get request. So you think I don't need to unsubscribe at all? I thought it's always needed unless you use async pipe. - Manuela
@Manuela doesn't hurt to unsubscribe, in fact if you don't want the logic within subscribe to run say if the api call hangs and you've moved to a different part of the application than it's a must. - Andrew Allen

3 Answers

1
votes

You can close the subscription once you get the response like as shown below. Also please note that HTTPObservable are autoclosable if you are calling AJAX API from the getCurrentValues() you need not to close the subscription as once you get the http response subscription is completed by the angular HTTP Client.

 updateValues(): void {
      if (this.isValuesUpdateRunning === false) {
        this.isValuesUpdateRunning = true;
        const sub = this.apiService.getCurrentValues().subscribe((data: any) => {
          if (data != null) {
            this.currentValues = data;
            this.isValuesUpdateRunning = false;
          }
         // closing the subscription right here:
         sub.unsubscribe();
        }, error => { sub.unsubscribe});

      }
    }
0
votes

Presuming that you are rendering currentValues in the template I recommend use of the async pipe.

In terms of the isValuesUpdateRunning logic, you can use exhaustMap and replace setTimeout with an RxJS interval

I've put a demo below - I highly recommend learning to mock an api service and try these things out for yourself.

const currentValues$ = interval(1000)
  .pipe(
    tap(i => console.log(`timer::${i}`)),
    exhaustMap(i => apiService.getCurrentValues(i)),  // ignores new values until api returns
    tap(apiData => console.log(`api::${apiData}`)),
    filter(data => !!data)                            // filter for truthy values
    takeUntil(stopClick$),                            // or this.destroyed$ component logic
  )

(this assumes async pipe used in template e.g.{{ currentValues$ | async | json }})

update - best to put takeUntil last!!

Stackblitz

References

exhaustmap - ignores new emitted values until current observable completes (angular http requests automatically complete)

0
votes

You don't need to unsubscribe from the service as @Kurt Hamilton mentioned if its using the angular http client. You need to stop the set interval which is creating the subscriptions.

Example using rxjs operators.

interval(1000)
   .pipe(
     takeWhile(() => this.isValuesUpdateRunning), // call subscription until the condition is false
     switchMap(() => this.apiService.getCurrentValues()),
     filter(Boolean), // check if response is not null
     tap(resp => { this.data = resp; this.isValuesUpdateRunning = false; }),
     // handle errors