Home > database >  What is the correct rxjs operator in case of nested subscription with if statement
What is the correct rxjs operator in case of nested subscription with if statement

Time:12-28

I have 2 subscription in an angular/typescript project.

These 2 subscription subscribe to each of their own observable A and B that is in outside of the component and in the service file.

Some times A change and B maybe not be change based on some component action and vice versa. However normally B changes after A changes in another component

Furthermore, B must do thing after checking the correct value of A . Therefore all I can think of is a nested subscription with an if statement like below.

ngOnInit(){
    this.A = this.serviceX.a.subscribe(
      data =>{ 
        this.temp = data;
        if(data=='correct'){
            this.B = this.serviceX.b.subscribe(
                beta => {
                    console.log('Sure' this.temp);
                }
            )
        }
      }
    )
}

While it works? But I have seen other post saying its not a good approach. So I tried combineLatest and forkJoin. But it seems they only works if both observable changes. And will not be called if one of the observable don't change at all. Correct me if I'm wrong.

So now all I can think of is seperate subscription and subscribe them one after another like below.

this.A = this.serviceX.a.subscribe(
  data =>{ 
    this.temp = data ;
  }
)
this.B = this.serviceX.b.subscribe(
  beta =>{ 
    if(this.temp=='correct'){
        console.log('Sure' this.temp)
    }
  }
)

But Im very concern I am doing it wrongly and making an unstable iteration. What exactly is the rxjs operator that I can use to make sure B subscription only subscribe after subscription A is done changing(However A maybe not be changed some time)? And eventually making the whole procedure more robust?

CodePudding user response:

May be you are looking for flatMap, you can map multi subscriptions, each flatMap should return it own Observable and only subscribe at end.

this.serviceX.a.pipe(flatMap(data => {
  this.temp = data;
  if (data == 'correct') {
    return this.serviceX.b.pipe(
      flatMap(beta => {
        this.temp2 = beta;
        console.log('Sure'   this.temp);
        return this.serviceX.c.pipe(map(delta => {
          this.temp3 = delta;
          console.log('Sure'   this.temp2);
          return delta;
        }));
      }));
  }
})).subscribe(delta => {
  console.log('Complete all subscriptions');
});

CodePudding user response:

Call a subscribe inside another subscribe is a badpractice, because you broke the stream and start with another stream (In your example the subscriber inside is never destroyed, this could cause memory problems).

Then you need to continue to manage your Stream with a flatMap or a switchMap operator and call only one Subscribe at the end.

For filter your stream, you can use the filter operator.

this.serviceX.a.pipe(                 
  filter((data: any) => data == 'correct'),  
  switchMap((value: any) => this.serviceX.b)     
)
.subscribe((value: any) => 
  console.log('Sure'   this.temp);     
); 

After that you have only 1 subscriber that you need Unsubscribe.

CodePudding user response:

Subscribing inside a subscribe-handler can get messy and I believe it can and should always be avoided. Your basic workflow - as I understood your code - is this:

  1. retrieve data from serviceX.a
  2. store data as side effect
  3. if data satifies some condition, retrieve data from serviceX.b
  4. actual subscribe handler

Now you can lookup an appropriate operator for each step. Here's how I'd do it:

this.serviceX.a.pipe(                 // 1 - load from a
  tap(data => this.temp = data),      // 2 - side effect
  filter(data => data == 'correct'),  // 3a - if
  concatMap(() => this.serviceX.b)    // 3b - load from b
).subscribe((beta) => 
  console.log('Sure'   this.temp)     // 4 - actual subscribe
); 

Note 1: You need only one subscription here.

Note 2: Depending on your use case you might want to replace concatMap with switchMap, exhaustMap or mergeMap.

Note 3: the usage of temp is quite dangerous here. Depending on which *map-operator you choose you could create a race-condition. While you wait for an emission of serviceX.b serviceX.a could already emit the next value and overwrite temp. If the only reason for temp is to store the result from serviceX.a, you should go with this solution instead: Is there an operator that lets me keep the initial value from a subscription after executing ,, switchMap"?

  • Related