0

I have approach according to the below.

this.service1.info
  .subscribe(a => this.service2.getDetails(a.id)
    .subscribe(b => {
      this.doStuff(b);
    })
  );

Recently, I noticed that we're going to have quite a few steps that gradually push more details to the client, so I'm anticipating the following atrocious pattern emerge.

this.service1.info
  .subscribe(a => this.service2.getDetails(a.id)
    ...
            .subscribe(z => { 
              this.doStuff (z);
            })
  );

Is there a nice trick in RxJS to handle such situations? I only need to perform an operation once all the steps in the consequential chain have been emitted/received. I've browsed through the docs but didn't find something that felt quite right. I suspect that it's there, just that my ignorance confuses me so I'm missing it.

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • Does this answer your question? [Angular 7 sequntially subscribing](https://stackoverflow.com/questions/56790962/angular-7-sequntially-subscribing) – R. Richards Dec 22 '19 at 18:14
  • @R.Richards Nope. As far I understood the examples, the guy has multiple **parallel** subscriptions. In my case, I have multiple **consequential** subsciptions. A typical scenario would be getting the ID from the route, using it to get the customer with that ID, then using it to get account number, finally using the number to access the latest transaction. None of the steps can be carried out prior to all the previous have completed but I wonder if there's a flatter syntax to express it. – Konrad Viltersten Dec 22 '19 at 18:22

1 Answers1

1

Yes, you are right. It is indeed an anti-pattern within RxJS to chain multiple subscribes.

A more elegant way of chaining these observable calls would be to make use of pipeable operators. In this scenario, the switchMap() operator would suffice.

As stated on the documentation, switchMap() will

Map to observable, complete previous inner observable, emit values.

this.service1.info
  .pipe(
    switchMap((a) => this.service2.getDetails(a.id)),
    switchMap((a) => this.service3.getDetails(a.id)),
    // switchMap((a) => this.service4.getDetails(a.id)),
    // subsequent chained methods
  ).subscribe(z => {
    // handle the rest when observables are returned
    this.doStuff(z);
  });

As you can see from the above pipe statement, we only call subscribe() once, and that will return the observable values. That is when we call the doStuff method.

wentjun
  • 40,384
  • 10
  • 95
  • 107
  • I might have answered a similar question over here (https://stackoverflow.com/questions/55447803/angular-subscribe-within-subscribe/55447947#55447947) – wentjun Dec 22 '19 at 18:16
  • Perhaps I'm just too dense to grasp it. Would you be willing to elaborate the example adding, say, two additional levels? How would it be if we have call to *service3*, *service4* and *service5*? Are you saying that we'd have to re-use *switchMap* three additional times? That's even more complexity than subsequent subscribes... :) – Konrad Viltersten Dec 22 '19 at 18:26
  • @KonradViltersten yes, sadly, it will require you to reuse `switchMap()` for the subsequent requests. I can assure you it is still less complex, and more readable than having mutliple `subscribe()` calls :) – wentjun Dec 22 '19 at 18:29