When using RXJS I ask myself if nested concatMap are equivalent to sequential ones. Consider the following example:
observable1.pipe(
concatMap(result1 => observable2.pipe(
concatMap(result2 => observable3.pipe(
concatMap(result3 => of(result3)
)
)
).susbcribe((result) => {...});
This would result in result
being result3
. In order to avoid the nesting, I would like to write the same as follows:
of(null).pipe(
concatMap(() => observable1),
concatMap(result1 => observable2),
concatMap(result2 => observable3),
concatMap(result3 => of(result3))
).susbcribe((result) => {...});
This will result in in result
being result3
as well. Even though there might be differences on a detail level, can I assume that both ways to concatenate observable are considered to be equivalent (e.g. with respect to failures)?
Any help is appreciated...
No, they don't behave the same way in all cases.
Nested ConcatMap
observable1.pipe(
concatMap(result1 => inner2)
)
inner2 = observable2.pipe(
concatMap(result2 => observable3.pipe(
concatMap(result3 => of(result3))
))
))
will map the next value from observable1
to inner2
when the previous inner2
observable completed.
Sequential ConcatMap
observable1.pipe(
concatMap(result1 => observable2),
concatMap(result2 => observable3),
concatMap(result3 => of(result3))
)
will map the next value from observable1
to observable2
when the previous observable2
completed.
The results you receive in the final subscribe callback will be the same and will arrive in the same order. But the time at which you receive those results may differ because the observable in Nested ConcatMap will probably map to subsequent inner2
observables later than the observable in Sequential ConcatMap will map to subsequent observable2
observables, as inner2
likely takes longer to complete than observable2
.
An Example of what events happen in which order using the three observables obs_1
, obs_2
and obs_3
: https://stackblitz.com/edit/rxjs-mxfdtn?file=index.ts.
With Nested ConcatMap obs_2
and obs_3
are part of the same inner observable (inner2
), so the 2nd inner2
is only subscribed to after the 1st inner2
(containing 1st obs_2
, 1st obs_3
, 2nd obs_3
) completed. With Sequential ConcatMap the 2nd obs_2
is subscribed to as soon as the 1st obs_2
completed.
obs_1: --11--12|
obs_2: --21--22|
obs_3: --31--32|
// Nested ConcatMap
❶
--11--12|~~~~~~~~~~~~~~~~~12
│ └--21--22|~~~22
│ │ └--31--32|
│ ❷ ❸ └--31--32|
└--21--22|~~~22 |
│ └--31--32|
└--31--32|
output: --31--32----31--32--------31--32----31--32|
// Sequential ConcatMap
❶
--11--12|~~~12
│ └--21--22|~~~21~~~~~~~~22
│ │ └--31--32|
│ ❷ └--31--32|
└--21--22|~~~22 ❸
│ └--31--32|
└--31--32|
output: --31--32----31--32----31--32----31--32|
x~~~x x is emitted at the first position but concatMap is holding the value
└O back and waits with ~ until a previous observable completes and then
maps the value to the observable O.
❶ start of the 1st obs_2. For Nested ConcatMap this also marks the start of
the 1st inner2.
❷ The 1st obs_2 completed but the 1st inner2 hasn't completed yet.
Nested ConcatMap waits until the 1st inner2 completes before it maps 12 to
the 2nd inner2. Sequential ConcatMap will map 12 to the 2nd obs_2 straight away.
❸ The 1st inner2 completed. Nested ConcatMap maps 12 to the 2nd inner2.
Sequential ConcatMap has already mapped 12 to the 2nd obs_2 by this time
and this 2nd obs_2 has already emitted both values and completed but concatMap
has buffered those values until now.
As you can see the Sequential ConcatMap approach subscribes to the 2nd obs_2
observable earlier than the Nested ConcatMap approach.