1

Is there a better way to re-write this code and avoid chaining of subscriptions ?

Why am I chaining? because I need to the output of source1$ in child subscriptions And also I have if conditions because I want to call child subscriptions conditionally

PS i checked solution in this post

Here is the stackblitz link and code

    import { from } from 'rxjs';

//emit array as a sequence of values
const source1$ = from([1]);
const source2$ = from([2]);
const source3$ = from([3]);

const useCond1 = true; // this is dynamic can be false too
const useCond2 = true; // this is dynamic can be false too

source1$.subscribe(val => {
  if (useCond1) {
    source2$.subscribe(() => {
      console.log('val from source1 in source2', val);
    });
  }

  if (useCond2) {
    source3$.subscribe(() => {
      console.log('val from source1 in source3', val);
    });
  }
});
dota2pro
  • 7,220
  • 7
  • 44
  • 79

2 Answers2

0

Not sure, but it seems that you need switchMap or mergeMap and iif

from rxjx doc:

import { fromEvent, iif, of } from 'rxjs';
import { mergeMap, map, throttleTime, filter } from 'rxjs/operators';

const r$ = of(`I'm saying R!!`);
const x$ = of(`X's always win!!`);

fromEvent(document, 'mousemove')
  .pipe(
    throttleTime(50),
    filter((move: MouseEvent) => move.clientY < 210),
    map((move: MouseEvent) => move.clientY),
    mergeMap(yCoord => iif(() => yCoord < 110, r$, x$))
  )
  .subscribe(console.log);
Andrey
  • 1,752
  • 18
  • 17
0

Yes, there is a better way!

RxJS provides many different operators and static functions for combining, filtering, and transforming observables. When you use what the library provides, you do not need to have nested subscriptions.

In general, I find it simpler to not do any logic at all inside the subscribe, but rather design observables that emit the exact data that is needed.

A simplistic example could look like this:

someValue$ = source1$.pipe(
  switchMap(val1 => useCond1 ? source2$ : of(val1))
);

someValue$.subscribe();

switchMap will subscribe to an "inner observable" whenever it receives an emission. The logic above says to either return the value emitted from source1$ (val1) or return whatever source2$ emits depending on the value of useCond1.

So source2$ will only get subscribed to when useCond1 is true;

Note: the function inside switchMap should return an observable (because switchMap subscribes to it), so of was used to turn the emitted value into an observable.


In your case, let's assume you want to emit some calculated value, based possibly on the other two sources.

We can use combineLatest to create a single observable based on 3 different sources. Since you only want to optionally call source2$ and source3$, we can define the sources based on your conditions. We can then use map to transform the array of values from the 3 sources, into the desired output:

someValue$ = source1$.pipe(
  switchMap(val1 => {
    const s1$ = of(val1);
    const s2$ = useCond1 ? source2$ : of('default val2');
    const s3$ = useCond2 ? source3$ : of('default val3');

    return combineLatest([s1$, s2$, s3$]);
  }),
  map(([val1, val2, val3]) => {
    return ... // your logic to return desired value
  })
);

combineLatest will emit an array containing the latest emissions from each source whenever any source emits. This means someValue$ will emit the latest calculated value whenever any of the sources change.

BizzyBob
  • 12,309
  • 4
  • 27
  • 51