Just as for javascript arrays the function flatMap exists similarly to flatten
higher level observables which are observables inside observables which
means inner observables. A good example of this is available in the
official documentation
const fileObservable = urlObservable.pipe(
map(url => http.get(url)),
);
Now http.get also emits an Observable which could emit one or many
strings So now you have a Observables of Observables.
There are different ways you would like to deal with this inner Observable according to your application.
We will go through them one by one along with examples
There are different ways you would like to deal with this inner Observable according to your application.
We will go through them one by one along with examples
1. mergeMap
const { fromEvent, interval } = rxjs;
const { scan, map, mapTo, mergeMap, take} = rxjs.operators;
const ones = fromEvent(document, 'click').pipe(mapTo(1));
// This observable gives a character everytime we click on the screen
const chars = ones.pipe(scan((acc, curr) => acc + curr, 64),
map(x => String.fromCharCode(x)));
/*This observable emits 5 alphanumeric outputs combining
the outer and the inner observable at the interval of
every 1 sec */
const alphanumeric = chars.pipe(mergeMap(char =>
interval(1000).pipe(take(5), map(x => char+x))));
alphanumeric.subscribe(x => console.log(x));
Sample Output:
A0
A1
A2
B0
A3
B1
A4
B2
C0
B3
C1
B4
C2
D0
C3
D1
C4
D2
D3
D4
Try out this code quickly in
this
plunker.
click on the screen 3 to 4 times in succession(may be not too rapidly)
We can observe that the inner observable of a stream of 5 numbers
(actually alphanumeric because we combine the outer with the inner observable)
starts every 1 second with the first click. The inner observable order is not important. As
soon the next click is registered the inner observable of that click also
starts emitting(it will start with B). Hence even if the earlier click emitting 5 numbers has not
finished and if you click before that then the next inner observable will be
emitted and the previous and current inner observables both will be actively
emitting values simultaneously until they complete. Similar is the case for
more number of clicks which means more inner observables and all of these
inner observables will be active at the same time.
2. concatMap
const { fromEvent, interval } = rxjs;
const { scan, map, mapTo, concatMap, take} = rxjs.operators;
const ones = fromEvent(document, 'click').pipe(mapTo(1));
// This observable gives a character everytime we click on the screen
const chars = ones.pipe(scan((acc, curr) => acc + curr, 64),
map(x => String.fromCharCode(x)));
/*This observable emits 5 alphanumeric outputs combining
the outer and the inner observable at the interval of
every 1 sec */
const alphanumeric = chars.pipe(concatMap(char =>
interval(1000).pipe(take(5), map(x => char+x))));
alphanumeric.subscribe(x => console.log(x));
Sample output:
A0
A1
A2
A3
A4
B0
B1
B2
B3
B4
C0
C1
C2
C3
C4
D0
D1
D2
D3
D4
Try out this code quickly in this plunker
click the screen a few times in a interval of a few seconds.
click the screen a few times in a interval of a few seconds.
We can see that inner observable order is maintained. If the first inner observable (on the first click A ) starts and before completing (i.e from A0 ... A4) if we click on the screen a second time or any number of times, the first inner observable is completed and only then the second inner observable starts. Same is the case with a third click, unless the first and second observables have completed in order the third observable will not start. So concatMap preserves the order of the inner observables.
3. switchMap
const { fromEvent, interval } = rxjs;
const { scan, map, mapTo, switchMap, take} = rxjs.operators;
const ones = fromEvent(document, 'click').pipe(mapTo(1));
// This observable gives a character everytime we click on the screen
const chars = ones.pipe(scan((acc, curr) => acc + curr, 64),
map(x => String.fromCharCode(x)));
/*This observable emits 5 alphanumeric outputs combining
the outer and the inner observable at the interval of
every 1 sec */
const alphanumeric = chars.pipe(switchMap(char =>
interval(1000).pipe(take(5), map(x => char+x))));
alphanumeric.subscribe(x => console.log(x));
Sample Output:
A0
A1
B0
B1
B2
C0
C1
C2
D0
D1
D2
D3
D4
Try out this code quickly in this plunker
Click on the screen once and before the first set of 5 outputs completes click on the screen again
We observe here that as we click on the screen first time the first inner observable starts emitting the 5 alphanumeric values.
But before this observable completes if we click on the screen a second time the second inner observable starts emitting and the
remaining values from the first inner observables are discarded. So at any point in time when new inner observable values are emitted
the earlier observable is stopped and the values from the current inner observable are emitted. On each new emission of the inner observable
(in our case each new click) the previous inner observable is cancelled and the values from this new observables start emitting.
Comments
Post a Comment