Understanding SwitchMap in rxjs
Asked Answered
E

2

5

as per the definition of the switchMap

On each emission the previous inner observable (the result of the function you supplied) is cancelled and the new observable is subscribed. You can remember this by the phrase switch to a new observable.

now, i have a code like this

const source = timer(0, 5000).pipe(map(x=>`${x}`));
const example = source.pipe(switchMap((f) => interval(1000).pipe(map(y=>`f${f}-s${y}`))),take(20));
const subscribe = example.subscribe(val => console.log(val+' '+ new Date().getSeconds()));

and the result is this

enter image description here

my question is,

  1. 25th second - the outer function is called and the inner function is not yet triggered, so the latest value of the outer function is 0 and inner function is also 0 as a default one because it has no value yet f0-s0 25

  2. 26th second - the inner function is called and the value ideally should be 0 as the function is just called first time but it is 1 ie. f0-s1 26

  3. 30th second - the outer function is called which resets the inner function value to 0, ie. f1-s0 30

  4. why is the inner function got reset on the 35th second, it is still 1 second remaining

stackblitz link

i find it very difficult to understand the concept, thank you

Eureetloir answered 5/4, 2019 at 8:9 Comment(1)
Apart from @martin 's answer explaining gaps on 34s and 39s, I think, you're also wrongly assuming that your source started emitting at 25th second, while it started at 24s. Theres no "default 0", it is source starting at 24s and interval first emitting at 25s.Cryoscopy
C
4

I think, you're wrongly assuming that your source started emitting at 25s second, while it started at 24s. And theres no "default 0".

Source emits at t0, then in 1s inner interval will emit. Inner interval will have 5 seconds to emit 4 or 5 times, before you'll switch to another value from the source timer. If your source timer started emitting at 24s, then first value you'll get on the subscription is at 25s — when the interval will emit its first value.

The reason for 4 or 5 time is in RxJS and JS scheduling. See this stackblitz for a rough example of what might be going on. Explaining this in detail would take more research effort and time.

Heres a marble diagram picturing mergeMap vs exhaustMap vs switchMap vs concatMap behavior to get a better understanding:

mergeMap vs exhaustMap vs switchMap vs concatMap

Check this mergeMap vs exhaustMap vs switchMap vs concatMap playground.

Cryoscopy answered 5/4, 2019 at 10:10 Comment(1)
all your examples using marbles were very helpful, thank youEureetloir
L
7

This is because RxJS by default for async actions uses setTimeout and setInterval function that don't guarantee they'll make exactly the timeout you want.

So if you use timeouts 5000 and 1000 then it's not guaranteed which of the actions will happen first after 5s. Sometimes the outer Observable emits first and sometimes the inner emits first but switchMap can't do anything about it.

You can see how different the times can get with eg. this:

const start = new Date().getTime();
setInterval(() => console.log(new Date().getTime() - start), 1000);

Live demo: https://stackblitz.com/edit/typescript-urep5j

1004
2001
3002
4000
4998
...

So sometimes the delay is 1004 and other times just 998.

Ladyinwaiting answered 5/4, 2019 at 9:19 Comment(5)
so in switchMap whenever the outer functions get called the inner functions value get reset, is my understanding right? if possible, could you make a marbles tom help people understand about switchMap, one like this?observable-playground.github.io/gist/…Eureetloir
@LijinDurairaj This shows what switchMap does reactivex.io/rxjs/img/switchMap.pngLadyinwaiting
i just found out this, rxmarbles.com/#switchMap, according to this marble, the switchMap only emits a value when the outer function emits a value and it resets the entire inner function value, is my understanding correct, please correct me if i am wrongEureetloir
I think, particular issue here is not in the JS scheduler missing 1s interval, but rather in the way RxJS interchangeably using setInterval and setTimer and the way these async tasks are prioritized in the JS env. When JS scheduler checks its task list -- it will run all timers and intervals that were set, but the order of the execution will rely on the way and time they were set. Heres a stackblitz example where order is swapped on consequent callsCryoscopy
@LijinDurairaj, "resetting inner function" is not the best way to think of switchMap. All __Map operators substitute value on source stream with a stream of values, returned by inner function. The difference is in reaction to subsequent values on source stream. switchMap will unsubscribe from previously swapped stream and use the new one, generated by inner function.Cryoscopy
C
4

I think, you're wrongly assuming that your source started emitting at 25s second, while it started at 24s. And theres no "default 0".

Source emits at t0, then in 1s inner interval will emit. Inner interval will have 5 seconds to emit 4 or 5 times, before you'll switch to another value from the source timer. If your source timer started emitting at 24s, then first value you'll get on the subscription is at 25s — when the interval will emit its first value.

The reason for 4 or 5 time is in RxJS and JS scheduling. See this stackblitz for a rough example of what might be going on. Explaining this in detail would take more research effort and time.

Heres a marble diagram picturing mergeMap vs exhaustMap vs switchMap vs concatMap behavior to get a better understanding:

mergeMap vs exhaustMap vs switchMap vs concatMap

Check this mergeMap vs exhaustMap vs switchMap vs concatMap playground.

Cryoscopy answered 5/4, 2019 at 10:10 Comment(1)
all your examples using marbles were very helpful, thank youEureetloir

© 2022 - 2024 — McMap. All rights reserved.