Is the order of observables given to combineLatest relevant? - rxjs

I have two observables which I want to combine with combineLatest:
const o1 = from(['a', 'b', 'c']);
const o2 = of('content from o2');
combineLatest(o1, o2)
.subscribe(result => console.log(result));
As I understood combineLatest, when any observable emits, it will combine the latest values of all observables and emit them:
When any observable emits a value, emit the latest value from each.
(https://www.learnrxjs.io/operators/combination/combinelatest.html)
However, with the code above, the output is:
["c", "content from o2"]
So only when o2 emits, combineLatest emits an value.
If I switch the order I hand over o1 and o2, it works as expected:
const o1 = from(['a', 'b', 'c']);
const o2 = of('content from o2');
combineLatest(o2, o1)
.subscribe(result => console.log(result));
Output:
["content from o2", "a"]
["content from o2", "b"]
["content from o2", "c"]
Is this expected behavior? If so, why? This seems to contradict the definition ob combineLatest.

Yes, in this case order matters, but it is a timing issue. combineLatest can not influence the order in which the values of the observables are emitted.
In your first case, o1 emits all values before o2 emits its value. In your second case o2 emits its value first and then o1 emits all its values. In real world examples you have a natural delay of observable values (service calls etc..) and so normally order does not matter.

I cannot stress how useful I've found combineLatestObject from rxjs-etc by #cartant.
You provide an object with named observables and then instead of getting an array back you get an object with the named values.
combineLatestObject({
isRaining: this.isRaining$,
isSnowing: this.isSnowing$
}).pipe(map(({ isSnowing, isRaining }) => {
return (isSnowing ? "It's snowing " : "It isn't snowing ") + "and " +
(isRaining ? "it's raining " : "it isn't raining");
});
Inside map() or switchMap() you can deconstruct the object like I did here, or just provide a single parameter where you can access the values map(data => data.isSnowing).
It's so much safer and easier to read - and when I look back at old code I did I find it so hard to read. Note in the example above I've deliberately put the parameters in a different order and no issues doing so.
Why this isn't part of the main library I don't know!

Related

Collect Emitted Values in a Sequence

I have 2 observables A and B which can emit at any time. But only when A emits a new value and then B emits a new value too, I collect these 2 values. If B just emits new values without A emitting new values first, I don't collect any values.
I know concatMap might be useful but it needs the previous observable to complete whereas in my case neither ever completes until everything is destroyed.
This can be modelled as projecting each element of A into the first arriving element of B, discarding any previous subscription to B when a new A arrives, e.g.:
A.pipe(
switchMap(x => B.pipe(
first(),
map(y => ({ a: x, b: y }))
)
)

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.

Please explain difference between RxJS map and switchMap as per example

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.

Emit the latest value of B, 1 second later after A has emmited a value

Let's say there are 2 observables, observable A and observable B.
When A emits a value, I want to wait 1 second, and then emit the latest value of B. If A emits another value while waiting 1 second, I want it to forget about the previous value and wait another 1 second (like switchMap does).
How could I achieve such behaviour?
It looks like you could do this easily with the withLatestFrom operator like the following:
const a$ = ...;
const b$ = ...;
a$
.switchMap(v => Observable.of(v).delay(1000))
.withLatestFrom($b.startWith(null), (a, b) => b)
.subscribe(...);

'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

Resources