Please explain difference between RxJS map and switchMap as per example - rxjs

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.

Related

How can of([1,2,3]).pipe(concatAll()) emit 3 values when of([1,2,3]) emits one?

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.

RXJS Buffer Transform but want initial value immediately

According to the documentation, the buffer transform will wait for a delay before emitting any values. What I'd like is to get the current value immediately, then only update every X seconds.
I've not been able to achieve this with rxjs yet. The closest I've come is to bind the observable then use a setTimeout function to rebind after the buffer timeout occurs. This has a side effect of clearing the current value for those X seconds before emitting the current values.
Any ideas?
Thanks!
Assuming by "current value immediately" you mean "first value as soon as it emits", you can buffer on the second element to the last, and merge in the first:
// source$: Observable<T>
const pub_source$ = source$.publish();
Observable.merge(
pub_source$.take(1).map(first => [first]),
pub_source$.skip(1).buffer(Observable.interval(X))
);
pub_source$.connect();
The source needs to be cold so that take(1) and skip(1) relate to the same element, so we use publish. The first element is also wrapped to keep the output type T[] consistent.
There's an easier way using the zip operator, see lightbulb note in learnrxjs
Combined with interval or timer, zip can be used to time output from another source!
// Useful for slow source that emits at around the same rate as interval
// but suffers back-pressure with fast-emitting source
const interval = 1000
const output = Observable.zip(source, Observable.timer(0, interval))
.map(x => x[0])
The Observable.timer 'regulates' the output from source. Note, timer's first parameter sets the delay for the first emit.
Working example: CodePen
Footnote
I just realized this will create back-pressure (build-up of un-emitted values) if you have a lot of events per second, so buffer is the better way to go with a fast emitting source.
// Buffered version for fast source
const output2 = source.buffer(Observable.timer(0, interval))
.filter(x => x.length) // filter out empty buffer emits
.flatMap(x => Observable.from(x)) // optional, converts array back to single emits

'of' vs 'from' operator

Is the only difference between Observable.of and Observable.from the arguments format? Like the Function.prototype.call and Function.prototype.apply?
Observable.of(1,2,3).subscribe(() => {})
Observable.from([1,2,3]).subscribe(() => {})
It is important to note the difference between of and from when passing an array-like structure (including strings):
Observable.of([1, 2, 3]).subscribe(x => console.log(x));
would print the whole array at once.
On the other hand,
Observable.from([1, 2, 3]).subscribe(x => console.log(x));
prints the elements 1 by 1.
For strings the behaviour is the same, but at character level.
Not quite. When passing an array to Observable.from, the only difference between it and Observable.of is the way the arguments are passed.
However, Observable.from will accept an argument that is
a subscribable object, a Promise, an Observable-like, an Array, an iterable or an array-like object to be converted
There is no similar behaviour for Observable.of - which always accepts only values and performs no conversion.
One line Difference :
let fruits = ['orange','apple','banana']
from : Emit the items one by one of array. For example
from(fruits).subscribe(console.log) // 'orange','apple','banana'
of : Emit the whole array at once. For example
of(fruits).subscribe(console.log) // ['orange','apple','banana']
NOTE: of operator can behave as from operator with spread operator
of(...fruits).subscribe(console.log) // 'orange','apple','banana'
Another interesting fact is Observable.of([]) will be an empty array when you subscribe to it.
Where as when you subscribe to Observable.from([]) you wont get any value.
This is important when you do a consecutive operation with switchmap.
Ex:
In the below example, I am saving a job and then sites, and then comments as a stream.
.do((data) => {
this.jobService.save$.next(this.job.id);
})
.switchMap(() => this.jobService.addSites(this.job.id, this.sites)
.flatMap((data) => {
if (data.length > 0) {
// get observables for saving
return Observable.forkJoin(jobSiteObservables);
} else {
**return Observable.of([]);**
}
})).do((result) => {
// ..
})
.switchMap(() => this.saveComments())
....
if there's no site to save, ie; data.length = 0 in addSite section, the above code is returning Observable.of([]) and then goes to save comments. But if you replace it with Observable.from([]), the succeeding methods will not get called.
rxfiddle
of will emit all values at once
from will emit all values one by one
of with spread operator = from operator
from: Create observable from array, promise or iterable. Takes only one value. For arrays, iterables and strings, all contained values will be emitted as a sequence
const values = [1, 2, 3];
from(values); // 1 ... 2 ... 3
of: Create observable with variable amounts of values, emit values in sequence, but arrays as single value
const values = [1, 2, 3];
of(values, 'hi', 4, 5); // [1, 2, 3] ... 'hi' ... 4 ... 5
from returns notification in chunks i.e. one by one.
for eg: from("abcde") will return a => b => c => d => e
of returns complete notification.
for eg: of("abcde") will return abcde.
https://stackblitz.com/edit/typescript-sckwsw?file=index.ts&devtoolsheight=100
The from operator takes source of events. from(source)
let array = [1,2,3,4,5]
from(array); //where array is source of events, array[of events]
let promise = new Promise(function(resolve, reject) {
// executor (the producing code, "singer")
});
from(promise); //where promise is source of event, promise(of event)
let observable = Observable.create(function(observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.next(4);
observer.next(5);
observer.complete();
});
from(observable); // where obsservable is source of events.
The of operator takes intividual events. of(event1, event2, event3)
of(1,2,3,4,5); // where 1,2,3,4,5 are individual events
I found it easier to remember the difference when the analogy with .call / .apply methods came into my mind.
You can think of it this way:
normally, all arguments, that are passed separately (separated by comma), are also emitted separately, in the order they were passed. of() just emits all arguments one by one as they are (like .call method passes arguments to the function it was called on)
from() is like .apply in a sense that it can take an array of values as an argument, and convert array elements into separate arguments, separated by comma.
So, if you have an array and want each element to be emitted separately, you can use from() or get the same behavior by using of() with spread operator, like of(...arr).
It's bit more complicated then that (from can also take observables) but with this analogy it will probably be easier to remember the main difference.
Yes it is true that of will result in an output in single go and from will happen one at a time. But there is more difference related to number of arguments and type of arguments.
You can pass any number of arguments to the Of. Each argument emitted separately and one after the other. It sends the Complete signal in the end.
However you can send only one argument to the from operator and that one argument should be a type of
an Array,
anything that behaves like an array
Promise
any iterable object
collections
any observable like object
For example you can send a raw object like
myObj={name:'Jack',marks:100}
to of operator to convert to Observable.
obj$:Observable<any> = of(myObj);
but you can not send this raw object myObj to from operator simply because it is not iterable or array like collection.
for more detail : visit here
from operator may accept one of
promises
iterable
arrays
observable
from emits each individual item from the observable , can also do conversions.
of operator takes in the raw value and emits the value from the observable.
import {from, Observable, of} from 'rxjs';
const ofObs = of([1,2,3]);
const fromObs = from([2,3,4]);
const basicObs = Observable.create(observer=>{
observer.next(100);
observer.next(200);
observer.next(300);
})
const promise = new Promise((resolve,reject)=>{
resolve(100);
})
const array = [1,2,3];
const iterbale = "Dhana";
// const myObs = from(ofObs);//possible and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = from(fromObs);//possbile and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = from(basicObs);//possbile and can emit individual item value everytime 100, then ,200 , then 300
const myObs = from(promise);//possible can emit value 100
// const myObs = array(promise);//possible and can emit individual item value everytime 1, then ,2 , then 3
// const myObs = iterable(promise);//possible and can emit individual item value everytime D then h then a then n then a
myObs.subscribe(d=>console.log(d))
import {from, of} from 'rxjs';
const basicOf1 = of([1,2,3,4,5,6]) // emits entire array of events
const basicfrom1 = from([1,2,3,4,5,6]) //emits each event at a time
const basicOf2 = of(1,2,3,4,5,6) // emits each event at a time
// const basicfrom2 = from(1,2,3,4,5,6) //throws error
//Uncaught TypeError: number is not observable
const basicOf3 = of(...[1,2,3,4,5,6]) // emits each event at a time
const basicfrom3 = from(...[1,2,3,4,5,6]) //throws error
//Uncaught TypeError: number is not observable
basicOf3.subscribe(d=>console.log(d))
Here is the link to codepen

What is the difference between mergeMap and mergeMapTo?

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);

alternative to zip that produces value whenever any of the observable emit a value

At the moment zip will only produce a value whenever all of the zipped observable produces a value. E.g. from the docs:
Merges the specified observable sequences or Promises into one
observable sequence by using the selector function whenever all of the
observable sequences have produced an element
I'm looking for an observable which can sort of zip an observable but will produce an array of sequence of the zipped observable wherein it doesn't matter if all produces a value..
e.g. lets say i have tick$, observ1, observ2.. tick$ always produce value every x secs.. while observ1 and observ2 only produces from time to time..
I'm expecting my stream to look like
[tick, undefined, observ2Res],
[tick, undefined, undefined],
[tick, observ1Res, observ2Res]
...
...
its not combine latest, given that combine latest takes the latest value of a given observable.
I believe buffer (or maybe sample) might get you on the right track. The buffer method accepts an Observable that's used to define our buffer boundaries. The resulting stream emits any items that were emitted in that window (example stolen from RXJS docs for buffer):
var source = Rx.Observable.timer(0, 50)
.buffer(function () { return Rx.Observable.timer(125); })
.take(3);
var subscription = source.subscribe(x => console.log('Next: ', x));
// => Next: 0,1,2
// => Next: 3,4,5
// => Next: 6,7
So we now have a way to get all of a stream's emitted events in a certain time window. In your case, we can use tick$ to describe our sampling period and observ1 and observ2 are our underlying streams that we want to buffer:
const buffered1 = observ1.buffer(tick$);
const buffered2 = observ2.buffer(tick$);
Each of these streams will emit once every tick$ period, and will emit a list of all emitted items from the underlying stream (during that period). The buffered stream will emit data like this:
|--[]--[]--[1, 2, 3]--[]-->
To get the output you desire, we can choose to only look at the latest emitted item of each buffered result, and if there's no emitted data, we can pass null:
const buffered1 = observ1.buffer($tick).map(latest);
const buffered2 = observ2.buffer($tick).map(latest);
function latest(x) {
return x.length === 0 ? null : x[x.length - 1];
}
The previous sample stream I illustrated will now look like this:
|--null--null--3--null-->
And finally, we can zip these two streams to get "latest" emitted data during our tick$ interval:
const sampled$ = buffered1.zip(buffered2);
This sampled$ stream will emit the latest data from our observ1 and observ2 streams over the tick$ window. Here's a sample result:
|--[null, null]--[null, 1]--[1, 2]-->

Resources