0

In my scenario, I have an higher order observable which when emits we needs to fetch two different pieces of data as inner observables.

something like

    $someDetails.pipe(
switchMap(sd => {
this.name = sd.name;
let innerObservableOne = getObservableOne(this.name);
let innerObservableTwo = getObservableTwo(this.name);
return {one: innerObservableOne, two: innerObservableTwo}
}).subscribe(r => ?????)

Can I get some lead on this?

Do I need to do something like this

https://medium.com/@snorredanielsen/rxjs-accessing-a-previous-value-further-down-the-pipe-chain-b881026701c1

The other option I have is to use two switchmaps one for each inner observable but that means two subscriptions which seems unnecessary.

$someDetails.pipe(
    switchMap(sd => {
    return getObservableOne(sd.name);
}).subscribe(.....);


$someDetails.pipe(
    switchMap(sd => {
    return getObservableTwo(sd.name);
}).subscribe(.....);

Edit1:

Any issues with following code:-

What about the following code.

details$ = $someDetails.pipe(share());

details$.pipe(switchMap(sd => getObservableOne(sd.name)).subscribe(...);
details$.pipe(switchMap(sd => getObservableTwo(sd.name)).subscribe(...);

Any thoughts or issues you see with this solution ?

tangokhi
  • 945
  • 2
  • 8
  • 20

1 Answers1

2

You want forkJoin:

import { forkJoin, interval, map, switchMap, timer } from 'rxjs'

interval(1000).pipe(
  switchMap(x => forkJoin([
    timer(100).pipe(map(() => `apple #${x}`)),
    timer(200).pipe(map(() => `orange #${x}`)),
  ])),
).subscribe(([apple, orange]) => console.log([apple, orange]))

This is the equivalent of Promise.all and will wait until multiple concurrent Observables have completed, then it will emit once, aggregating the last values emitted by each "inner query".

In the example above, every second (with interval(1000)), I spawn two new queries, fetching an apple and an orange, respectively. The query that retrieves an apple completes in 100ms, the one for the orange takes 200ms (with timer).

For completeness' sake, here's the output of the above example:

[ 'apple #0', 'orange #0' ]
[ 'apple #1', 'orange #1' ]
[ 'apple #2', 'orange #2' ]
[ 'apple #3', 'orange #3' ]
[ 'apple #4', 'orange #4' ]
...
ccjmne
  • 9,333
  • 3
  • 47
  • 62
  • Thanks for the prompt reply. One question though. what if the inner observable does not complete? forkJoin might be an issue then ? – tangokhi Mar 16 '23 at 14:49
  • Ah sorry for not seeing your message before! If an inner Observable doesn't complete, the corresponding `[apple #1, orange #1]` will hang "forever"... it would need *both* observables to complete, you're right. However, that is, until one second later, when `interval(1000)` emits again, and `switchMap` will take over: if `forkJoin` is still pending, `switchMap` will cancel the on-flight request by unsuscribing from the `forkJoin` that is going to hang forever, create a new `forkJoin` of two new requests for a new apple and a new orange, and subscribe to that one. – ccjmne Mar 17 '23 at 15:52
  • Basically, assuming that the request for `apple #1` never completes, your output there would be: after 1.2s: `[ 'apple #0', 'orange #0']`, after 2.2s: nothing, after 3.2s: `[ 'apple #2', 'orange#2' ]`. It essentially would skip waiting around for the queries that aren't complete by the time the next `switchMap` is triggered. – ccjmne Mar 17 '23 at 15:59
  • Here's a link to some online service where this case is illustrated: https://playcode.io/1316439. Have fun! – ccjmne Mar 17 '23 at 16:03