Passing value from one observable to another in RxJS

While creating a web application using RxJS such as an Angular 2/4/6 application, you might come across situations in which you need to pass value from one observable to another dependent observable and then utilize the final result into the application. For example you may want to send an ajax request based on the result of another ajax request and then show the result of second request on the UI.

The simplest solution that comes to mind for the situation is through nested subscriptions, as is shown in the example below:-
this.request1(...).subscribe((response1) => {
    this.request2(response1).subscribe((response2) => {
        console.log(response2);
    });
});

Avoid using nested subscriptions


Although it will work fine for observables that completes after emitting singe value, but this is not the recommended way and neither it will produce desired result in most of the cases, specially when we are working with observables that emits more than one value.

Consider an example in which we have 2 observables :- let "observable1" represent the outer observable and "observable2" represents the inner observable that is supposed to be dependent on "observable1" i.e. it should emit values based on the current value of  "observable1". So going by the nested subscriptions approach an implementation of such an example would be :-

var observable1 = Rx.Observable.interval(6000);
var observable2 = Rx.Observable.interval(2000);

observable1.subscribe((ob1Val) => {
    console.log("observable1:", ob1Val);
    observable2.subscribe((ob2Val) => {
        console.log("observable2:", ob1Val, ob2Val);
    });
});

You can run the live example of above code at this link.

Since the "observable2" is expected to emit values based on the current value of  "observable1", the expected output of above example should be :-

"observable1:" 0
"observable2:" 0 0
"observable2:" 0 1
"observable1:" 1
"observable2:" 1 0
"observable2:" 1 1
"observable1:" 2
"observable2:" 2 0
"observable2:" 2 1
...

But when we actually run the code the output comes out to be :-
"observable1:" 0
"observable2:" 0 0
"observable2:" 0 1
"observable1:" 1
"observable2:" 0 2
"observable2:" 1 0
"observable2:" 0 3
"observable2:" 1 1
"observable2:" 0 4
"observable1:" 2
"observable2:" 1 2
"observable2:" 0 5
"observable2:" 1 3
"observable2:" 2 0
"observable2:" 0 6
"observable2:" 1 4
"observable2:" 2 1
"observable2:" 0 7
...

This is not at all the output we wanted. So what's exactly happening here is that a new subscription of  "observable2" is created for each emitted value of "observable1" and all these subsciptions of "observable2" are running in parallel to each other and that's why we got the output as we see.

RxJS operators to the rescue


So what we can do to avoid such a situation is we should use appropriate RxJS operator instead of nested subscriptions. The operator to go for in our current situation is the switchMap operator. So using the switchMap operator the above code can be rewritten as :-

var observable1 = Rx.Observable.interval(6000);
var observable2 = Rx.Observable.interval(2000);

observable1.switchMap((ob1Val) => {
    console.log("observable1:", ob1Val);
    return observable2.map((ob2Val) => [ob1Val, ob2Val]);
})
.subscribe((mappedVal) => {
    console.log("observable2:", mappedVal[0], mappedVal[1]);
});

You can run the live example of above code at this link.

The output of this code is :-

"observable1:" 0
"observable2:" 0 0
"observable2:" 0 1
"observable1:" 1
"observable2:" 1 0
"observable2:" 1 1
"observable1:" 2
"observable2:" 2 0
"observable2:" 2 1
...

Exactly the same output as we wanted it to be. I recommend reading more about RxJS operators and get familiar with them, some useful links to get started with are:-





No comments:

Post a Comment