In rxjs5 doc, it mentions 'To reduce polymorphism and get better performance out of operators, some operators have been split into more than one operator'. What does it actually mean and how to use the mergeMapTo operator?
From the docs, mergeMapTo:
It's like mergeMap, but maps each value always to the same inner Observable.
I see mergeMapTo as a shortcut to always output the same value. mergeMapTo doesn't care about the source value.
Also from the docs:
Maps each source value to the given Observable innerObservable
regardless of the source value, and then merges those resulting
Observables into one single Observable, which is the output
Observable.
You'll see that mergeMap takes a function while mergeMapTo takes a value:
An example with mergeMap (we're transforming values):
Rx.Observable.of(1, 2, 3).mergeMap(x =>
Rx.Observable.interval(1000).map(i => x+i)
);
While using mergeMapTo we can take values from a stream and always output the same value (also transforming, but always to the same value):
Rx.Observable.of(1, 2, 3).mergeMapTo(10);
Related
of([1,2,3]).subscribe(console.log)
prints:[1,2,3]
But:
of([1,2,3]).pipe(concatAll()).subscribe(console.log)
prints:
1
2
3
Why the above happens? Why adding concatAll() emits the elements of the array one by one? Isn't this somehow the opposite of what the word concat means?
I feel that concatAll() acts differently depending on the input.
Consider also this:
from([of(1),of(2),of(3)]).pipe(concatAll()).subscribe(console.log)
It will again print:
1
2
3
So of([1,2,3]).pipe(concatAll()) == from([of(1),of(2),of(3)]).pipe(concatAll())
But of([1,2,3]) != from([of(1),of(2),of(3)]) because subscribing to the latter will print:
Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }
The right side of the above equality is pretty clear to me, but where is documented that concatAll() should emit all the values of the array separately, acting like a pipeable from?
All the operators that deal with higher-order observable operators, perform the same conversion that RxJS's from operator does.
that is:
mergeMap(_ => ([1,2,3]))
Is basically the same as
mergeMap(_ => from([1,2,3]))
That works the same when merging/switching/concatenating promises and other iterables.
The difference you're seeing in behaviour is also attributed to the fact that from creates a new observable, while the higher-order observable operators tend to create a new observables and define behaviour around how those observables are subscribed to.
so concatAll is adding extra behaviour.
Finally, just because two things have similar outputs for a given example, doesn't mean they're doing the same thing under the hood.
of([1,2,3]).pipe(concatAll()) and from([of(1),of(2),of(3)]).pipe(concatAll()) describe two very different set of behaviours that in (this case) give you the same output to the console.
The first emits an array and has that array turned into a stream of numbers by concatAll(). The second emits three observables, and has has concatAll() subscribe to each only when the previous one completes. They complete after emitting just one number.
You can see the distinction clearly with your second example:
of([1,2,3]) and from([of(1),of(2),of(3)])
The first emits and array, the second emits three observables. That hasn't changed.
concat([1,2,3]), &&
concat(of(1), of(2), of(3))
Emit the same output again as well.
I've seen many other posts on this topic, and have read the official (see below) and semi-official documentation such as https://www.learnrxjs.io/operators/transformation/switchmap.html, but I still haven't been able to internalize the difference between "map" and "switchMap" and am hoping to clarify with the concrete examples below.
Note as per official RxJS documentation:
map: "Applies a given project function to each value emitted by the source Observable, and emits the resulting values as an Observable." http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map
switchMap: "Projects each source value to an Observable which is merged in the output Observable, emitting values only from the most recently projected Observable." http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-switchMap
With my incomplete understanding in mind, I made a few very simple examples, see StackBlitz https://stackblitz.com/edit/rxjs-xpicph?devtoolsheight=60, but still don't fully understand why some of the examples are producing the output they do, in the way they do.
Firstly, some very simple string examples:
// String Example 1
const source = of('World').pipe(
map(x => `Hello ${x}!`)
);
source.subscribe(x => console.log(`SOURCE (map): ${x}`));
// SOURCE (map): Hello World!
OK, fair enough, I think I mainly get this.
"Of" emits 'World' as value (or is the emission still an Observable at this stage?) to "pipe"
"pipe" provides 'World' as an value (or is it still an Observable?) to "map"
"map" projects this (value? Observable?) to 'Hello World', waiting for all the characters to complete, and returns this (value? Observable?) to "pipe"
"pipe" then returns an Observable.
Hence we get the output: "Hello World"
// String Example 2
const otherSource = of('World').pipe(
switchMap(x => `Hello ${x}!`)
);
otherSource.subscribe(x => console.log(`SOURCE (switchMap): ${x}`));
// SOURCE (switchMap): H
// SOURCE (switchMap): e
// SOURCE (switchMap): l
// SOURCE (switchMap): l
// SOURCE (switchMap): o
// SOURCE (switchMap):
// SOURCE (switchMap): W
// SOURCE (switchMap): o
// SOURCE (switchMap): r
// SOURCE (switchMap): l
// SOURCE (switchMap): d
// SOURCE (switchMap): !
WHOA! EXCUSE ME? WHAT JUST HAPPENED?
"Of" emits 'World' as value (or is the emission still an Observable at this stage?) to "pipe"
"pipe" provides 'World' as an value (or is it still an Observable?) to "switchMap",
"switchMap" projects this (value? Observable?) to 'Hello World', but unlike "map" doesn't wait for all the characters to complete before outputting to "pipe" a series of Observables (or values?), and is it one Observable per character? or is it one Observable that emits once per each character?
"pipe" then returns an Observable on each character?
QUESTION: What exactly is going on under the hood here, step-by-step in the chains above?
Let's move on to another simple set of examples, and then hopefully try to tie everything together:
// OBJECT EXAMPLES
const foo = {
"first": 1,
"second": 2
}
// OBJECT EXAMPLE 1
Object.keys(foo).forEach(obj=>of(foo[obj]).pipe(
map(x=>x*2)
).subscribe(x => console.log(`SOURCE (map): ${x}`)))
// SOURCE (map): 2
// SOURCE (map): 4
OK, fair enough. That's seems pretty straightforward
// OBJECT EXAMPLE 2
Object.keys(foo).forEach(obj=>of(foo[obj]).pipe(
switchMap(x=>of(x*2)) // WHY DO WE NEED ANOTHER "of()" HERE? "switchMap(x=>x*2)" DOESN'T COMPILE
).subscribe(x=> console.log(`SOURCE (switchMap): ${x}`)))
// SOURCE (switchMap): 2
// SOURCE (switchMap): 4
Reasonably clear, but WHY do we need to supply "of(x*2) to "switchMap"? In STRING EXAMPLE 2, "switchMap" seemed to emit like crazy and automatically wrap its output as Observable (or did "pipe" wrap the output as Observable?), but whatever the case, "switchMap" and "pipe" didn't need any extra "of()" or any other help wrapping the output as an Observable, but in OBJECT EXAMPLE 2, we explicitly need to provide the second "of()" to make sure the the output from "switchMap" is an observable, otherwise the code won't compile. (But for "map", we don't need to provide the second "of()"). Again, step-by-step, why the difference?
So, to summarize, I'd be extremely grateful if anyone can explain:
At which point(s) in the chain(s) in the examples above are we dealing with values (i.e., emissions from Observables) and at which points with Observables?
Why does "switchMap" provide the apparent parsing behavior seen in STRING EXAMPLE 2?
Why do we need to provide "of()" to "switchMap" in OBJECT EXAMPLE 2, but not in STRING EXAMPLE 2? (And, similarly, why doesn't "map" need a second "of()"?)
Thank you in advance!!
For your example operators, these are the type conversions:
of: receives a parameter of type T, produces a single notification of type T, then completes
map: receives a parameter of type T => R, produces a notification of type R whenever it receives a notification of type T
switchMap receives a parameter of type T => ObservableLike<R>, produces a notification of type R whenever it receives a notification of type T
I think that's the main confusion here. switchMap has a projection function that's expects ObservableLike<R> as a return type. The important part of this statement is ObservableLike.
It's sometimes counter-intuitive, but RxJS internally converts other types in Observables when possible, like:
Array or
Promise
When an Array is used in a place where ObservableLike can be provided, RxJS treats the array as a stream of values.
For example, say I converted this array into an observable:
from([1, 2, 3])
what I'd get on subscribe would be:
// 1
// 2
// 3
And as string is nothing more than Array<char>, RxJS tries to convert this string into a stream of it's characters. (char is not really a data type in JS, but string internally is an array)
i.e. these are equal:
from(['a', 'b', 'c'])
from('abc')
I think that's explained with the above answer. You always need to provide an ObservableLike for switchMap, but the string type happens to be an ObservableLike - just a stream of characters.
I tried to google it, but I cannot find any clear answer for that.
From the documentation, I noticed that one is operator, and one is function.
What is the difference between them? and what should I use in my code?
Thanks!
Here is the documentation link:
https://rxjs-dev.firebaseapp.com/api/operators/timeInterval
https://rxjs-dev.firebaseapp.com/api/index/function/interval
interval() is a so called Observable creation method that returns an Observable that emits periodically an ever increasing sequence of numbers with a constant delay between them.
timeInterval() is an operator that basically "timestamps" each emission from its source with time between the two most recent emissions.
The main and probably more obvious difference is how you use them:
range(1, 20).pipe(
timeInterval(), // `timeInterval()` is an operator
).subscribe(...); // TimeInterval objects
interval(1000).pipe( // `interval()` is a source Observable
).subscribe(...); // 0, 1, 2, ...
I have a hard time understanding something related to using a concat function inside a mergeMap operator.
The documentation for concat says that
You can pass either an array of Observables, or put them directly as arguments.
When I put the Observables directly as arguments, like in the following example, I correctly get 20 and 24 in the console.
of(4)
.pipe(
mergeMap(number => concat(of(5 * number), of(6 * number)))
)
.subscribe(value => console.log(value));
But when I put them as an array, then in the console I get the Observables and not their values:
of(4)
.pipe(
mergeMap(number => concat([of(5 * number), of(6 * number)]))
)
.subscribe(value => console.log(value));
Here's a live version in Stackblitz.
Any idea why is that? Shouldn't both examples work identically?
Those two scenarios are different and they should not work identically. concat takes Observables as arguments and it will sequentially subscribe to those streams and only subscribe to the next Observable when the previous one completed. Every operator or creation method returns an Observable. This means that in the first example, when you are using concat, it will return an Observable that emits 20 and then 24. Because you are dealing with a nested Observable you have to use mergeMap which will subscribe to the resulting Observable returned by concat.
Now in the second example, if you pass in an array, concat will convert this (using from() internally) to an Observable that emits 2 values, and those values are Observables again. So you have 3 levels of nesting here. The first is the most outer Observable, the source, which of(4), the second level is the one you map to inside your mergeMap and the third in the second example are the Observables inside your array. The thing is you only flatten the levels up to level 2 but not the 3rd level. Again, in your second example the Observable returned by mergeMap emits two Observables but those are just the proxies and not the values emitted by these Observables. If you want to subscribe to those as well, you could chain on another mergeMap like so
concatArray() {
of(4)
.pipe(
mergeMap(number => concat([of(5 * number), of(6 * number)])),
mergeMap(x => x)
)
.subscribe(value => console.log(value));
}
Another way is to spread the array so that concat does not receive an object that is ArrayLike but rather Observables directly:
concatArray() {
of(4)
.pipe(
mergeMap(number => concat(...[of(5 * number), of(6 * number)]))
)
.subscribe(value => console.log(value));
}
Both will print out:
20
24
I hope this makes this a little bit more clear and describes the differences between the first and the second example.
In Rx.js, how to turn a stream of arrays into array of streams for example i have a stream of following:['0a','0b'], ['1a','1b'],['2a','2b','2c'] and i want to get the following streams:
0a---1a---2a--->
0b---1b---2b--->
2c--->
Are there any operators for doing something like that or should I write one from scratch?
Something like this should work
stream.
flatMap(array =>
Rx.Observable.from(
array.map((obj, i) => {index: i, ...obj})
)
).groupBy(x => x.index, ).
subscribe(x =>
x.map((x,i) => subscribe(x))
)
You can achieve it relatively easily with the existing operators.
What you want to achieve is very similar to what is described here : RXJS: alternately combine elements of streams
It suggests two ways :
using the Rx.Observable.zip operator (takes as argument an array of observables and emit a stream of arrays whose element at index n is the xth value emitted by the nth observable)
That solution however, applied in your example will stop at 1a,1b because the resulting observable will complete as soon as one of the observable completes.
extending your arrays to give them all the same length by completing with dummy values, and applying the Rx.Observable.zip operator
In those both options :
if you remove the last line, .concatMap.... you will get a stream of array like [0a,0b], [1a,1b], [2a,2b,2c] from which you can easily map by index (.map(function(array){return array[N];}) will get you [Na,Nb...]) to get the stream which you want.
OR you can keep the exact same code and add .filter(function(value,index){return index % N == I}), where N is the number of streams, and I is the stream you want, i.e. the stream with values (Ia,Ib...)
Documentation about the zip operator > http://reactivex.io/documentation/operators/zip.html https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/zip.md,