Collect Emitted Values in a Sequence - rxjs

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

Related

Execute array of observables in sequence based on condition

Given an array of observables where each observable can emit EMPTY, how can I execute the next observable in the array on the condition that the previous observable returned EMPTY or stop iterating the array and return a non EMPTY value once an observable in the array has emitted the first non EMPTY value?
Here would be one approach:
const emptyVal = Symbol('empty');
const src$ = concat(
...arrOfObservable.map(
obs$ => obs$.pipe(
last(null, emptyVal)
)
)
).pipe(
filter(v => v !== emptyVal),
first()
)
The last() operator will emit the last emitted value, after the source completes. If there is no last value(obs$ has used EMPTY), it will emit emptyVal.
filter(v => v !== emptyVal) will make sure that we keep iterating until we get a non empty value.
With first(), we'd stop the iteration and we'd get the emitted value.
Note: this approach works if the obs$ completes at some time, otherwise last won't be able to do its job.

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

Is the order of observables given to combineLatest relevant?

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!

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

Start emitting after another observable emitted its first item

I have this simple usecase that I can't seem to figure out.
Given 2 observables, A and B
A starts emitting items after B emits it's first item, and B can continue to emit items, which should no longer affect A.
I need this because A needs the first value emitted by B to start emitting.
I've tried both skipUntil and startWith to no avail.
I think flatMap can help you do what you want:
Observable<Integer> createA(Observable<Integer> B) {
return B.take(1).flatMap(b -> {
/* replace with your Observable A implementation here */
BehaviorSubject<Integer> subjectA = BehaviorSubject.create();
return subjectA;
});
}
This method returns an Observable A, which begins emitting only after it has been initialized with the first value emitted by Observable B.

Resources