concat 2 observable, and get booth their value in the same time - rxjs

Let's say I have the following : https://stackblitz.com/edit/concat
import {concat} from "rxjs/observable/concat";
import {fullObserver, stream} from "./utils";
const a = stream('a', 200, 3, 'partial');
const b = stream('b', 200, 3, 'partial');
concat(a, b).subscribe(fullObserver('concat'));
This will, subscribe to a, give me the result of a, then subscribe to b, and get the b result.
but, how do I do the follow,
subscribe to a => a complete
subscribe to b => b complete
get the result of a and b in an array like when booth are completed
its like a forkJoin but with a concat, so that all subscription are sequential. How could I do ?

There's toArray() operator that will collect all next notifications and re-emit them in a single array after its source completes (concat will complete after all its source complete).
So in your case it will look like this:
concat(a, b).pipe(toArray()).subscribe(fullObserver('concat'));
Btw, you don't need to be importing "creation operators" such as concat or timer from rxjs/observable/* and you import them from just rxjs.

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

Only activate observable if another observable has emitted

Here's the scenario: there are two observable streams (A and B). I want to only emit from B after A has emitted at least once.
I tried combineLatest, but the problem with that was that the combined stream emitted when either A or B emitted. I only want this stream to emit when B emits, I do not need the value from A. I just need it to have been emitted once.
This is what I am looking for:
---a---a-- A
-b---b---- B
---b-b---- required
I think this should work:
combineLatest(a$.pipe(first()), b$)
.pipe(
map(([a, b]) => b)
)
combineLatest will emit for the first time when both a$ and b$ would have emitted, then it will only emit when b$ emits, due to a$.pipe(first()).
Once the combine latest emits then switch back to b.
combineLatest(a$, b$).pipe(
map(([a, b]) => b),
switchMap(_ => b$)
);

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

Wait for set of emmisions to occur in Observable

Given a stream that pushes items into Observable A.
How to notify subscribers when there are exactly 3 items pushed in A?
To visualize it:
--o--i--o------i---------o----i------|---> A
I need to wait for all i to arrive even though there are other items present such as o.
And if there are no exactly 3 is pushed within a timeframe, then fire an error to redo the procedure.
Thanks.
I'd use filter() to filter only i characters, count() to count all items emitted until the source completes and concatMap() to decide whether the number of items is correct and eventually throw an error.
import {Observable} from 'rxjs';
Observable.from(['o', 'i', 'o', 'i', 'o', 'i'/*, 'i'*/])
.filter(val => val == 'i')
.count()
.concatMap(count => count == 3 ? Observable.of(count) : Observable.throw('Expected 3 values'))
.subscribe(
count => console.log(count),
err => console.log(err)
);
Which prints to console:
3
And if you add one more i to the source Observable the subscriber receives an error:
Expected 3 values
See live demo: http://plnkr.co/edit/VkQeGpY2gDj3gFhXD2uR?p=preview
If you want to resubscribe when an error happens use retry() or retryWhen(). This another demo fails first because it contains only 2 is but it resubscribes with retry() and then succeeds.
Here's more complicated demo: http://plnkr.co/edit/ID5WTVYGYZuhEJ7fXn1F?p=preview

Resources