36

What's the way to simplify something like the following code example? I can't find the right operator.. could anyone give a short example?

this.returnsObservable1(...)
  .subscribe(

    success => {

      this.returnsObservable2(...)
        .subscribe(

          success => {

            this.returnsObservable3(...)
              .subscribe(

                success => {
                   ...
                },
Nikita Fedyashev
  • 16,963
  • 11
  • 45
  • 76
Max Solid
  • 1,093
  • 2
  • 19
  • 31

4 Answers4

38

As mentioned in comments, you are looking for the flatMap operator.

You can find more details in previous answers :

Your example would read as :

this.returnsObservable1(...)
  .flatMap(success => this.returnsObservable2(...))
  .flatMap(success => this.returnsObservable3(...))
  .subscribe(success => {(...)}); 
Robin Dijkhof
  • 17,778
  • 11
  • 60
  • 104
user3743222
  • 17,757
  • 5
  • 64
  • 73
  • 2
    @Robin Dijkhof So a function which is returning observable is used inside flatmap, ```.flatMap(success => this.returnsObservable2(...))``` then `this.returnsObservable2(...)` don't i need to subscribe it ? – Vikas Kalapur Aug 18 '19 at 18:52
30

Previously answered for RxJS 5, I ended up on this page whilst using 6.

In the case you're on 6 as well (and I think you should be by now) you can use flatmap as an operator within pipe.

Modified the example code of @user3743222:

this.returnsObservable1(...)
  .pipe(
    flatMap(success => this.returnsObservable2(...)),
    flatMap(success => this.returnsObservable3(...))
  )
  .subscribe(success => {(...)}); 
Mathijs Segers
  • 5,962
  • 9
  • 48
  • 71
7

The switchMap operator can also be useful. Some examples which describe the usefulness of switchMap compared to nested subscriptions can be found here:

  1. situation with nested subscriptions

This codepen gives a demo: https://codepen.io/anon/pen/zdXBvP?editors=1111

Rx.Observable
  .interval(5000)
  .subscribe((val) => {
    console.log("outer:", val);
    Rx.Observable
      .interval(1000)
      .subscribe((ival) => {
        console.log("inner:", val, ival); 
      });
  });
  1. situation with switchMap

This codepen gives a demo: https://codepen.io/anon/pen/xLeOZW?editors=1111

Rx.Observable
  .interval(5000)
  .switchMap((val) => {
    console.log("outer:", val);
    return Rx.Observable.interval(1000).map((ival) => [val, ival]);
  })
  .subscribe((val) => {
    console.log("inner:", val[0], val[1]);
  });
sdec
  • 113
  • 1
  • 5
  • Examples aren't necessarily enough, but thanks for sharing. – spakmad Sep 07 '18 at 05:13
  • A general pattern I've found is to replace all "subscribes" with "switchMap" returning the next inner Observable. Then tack the most nested "subscribe" onto the end of the chain – reggaeguitar Dec 13 '18 at 18:57
1

You basically need sequencing. With RxJS concat, you can just do this:

import { concat } from 'rxjs';
...

concat(
  this.returnsObservable1(...), 
  this.returnsObservable2(...), 
  this.returnsObservable3(...), 
  ...
)
.subscribe(success => {(...)});

If your subsequent observables need to know the values from the previous ones, you could use the RxJS concatMap operator.

import { concatMap } from 'rxjs/operators';
...

this.returnsObservable1(...)
  .pipe(
    concatMap(result1 => this.returnsObservable2(...)),
    concatMap(result2 => this.returnsObservable3(...)),
    ...
  )
  .subscribe(success => {(...)});

concat: https://www.learnrxjs.io/learn-rxjs/operators/combination/concat

concatMap: https://www.learnrxjs.io/learn-rxjs/operators/transformation/concatmap

Ε Г И І И О
  • 9,464
  • 1
  • 42
  • 52