Home > Net >  Subscribe in subscribe in Angular rxjs
Subscribe in subscribe in Angular rxjs

Time:11-05

I have:

getSth(): void {
    this.service.functionName.pipe(
      takeUntil(this.destroy$),
      distinctUntilChanged(),
      map(res => res.event)
    ).subscribe(((response) => {
      this.getAnother(response);
    }));   }

getAnother(response): void {
    this.anotherService.anotherFunctionName(response).subscribe((res) => {
      this.result = res;
    });   }

I know that isn't a good solution to write subscribe in subscribe. How to fix it?

CodePudding user response:

Let's use switchMap :

getSth(): void {
  this.service.functionName.pipe(
    takeUntil(this.destroy$),
    distinctUntilChanged(),
    map(res => res.event),
    switchMap(response =>
      this.anotherService.anotherFunctionName(response)
    )
  ).subscribe(response => this.result = response);
}

CodePudding user response:

You should aim to chain observables together where possible, with only one subscriber at the top level. There are many good reasons for this, beyond the scope of this answer.

You could refactor your code to return an observable from your function (using tap to perform any side-effects such as updating state), and use switchMap to chain it onto the existing observable.

getSth(): void {
    this.service.functionName.pipe(
      takeUntil(this.destroy$),
      distinctUntilChanged(),
      map(res => res.event),
      switchMap(response => this.getAnother(response))
    ).subscribe();   
}

// TODO: return correct type in Observable<T>
getAnother(response): Observable<any> {
  return this.anotherService.anotherFunctionName(response).pipe(
    tap(res => this.result = res)
  );   
}

CodePudding user response:

You have more options, there are some *map operators, that handle the flow differently. By your example you could use switchMap, which cancels your running observable (in your example the getAnother). There's a Operator Decision Tree in the docs, try it, it can help a lot.

You get switchMap with this logic:

  1. I have one existing Observable, and
  2. I want to start a new Observable for each value
  3. and cancel the previous nested Observable when a new value arrives
  4. where the nested Observable is calculated for each value

One other note, you should have your takeUntil to be placed last.

You could write something like this:

getSth(): void {
  this.service.functionName.pipe(
    // If service.functionName returns a new object every time distinctUntilChanged will do nothing as references won't be the same.
    // distinctUntilChanged(),
    switchMap(resp => this.anotherService.anotherFunctionName(resp.event))
    takeUntil(this.destroy$),
    ).subscribe((result) => {
      this.result = result;
    }));
}
  • Related