I need data from two sources to combine into one res: {data1: any, data2: any}
object, and I need to achieve this even if one of the sources doesn't emit any values
Here's the structure I expect:
xxx (
source1,
source2,
(data1, data2) => ({data1: data1, data2: data2})
).subscribe(res => {
doSomething1(res.data1)
doSomething2(res.data2)
})
Is there any rxjs operators that can do this?
currently I can solve this with combination of startWith
and combineLatest
- I emit null value so combineLatest
can start emitting values - any better way to solve this without startWith(null)
?
combineLatest (
source1.pipe(startWith(null)),
source2.pipe(startWith(null)),
(data1, data2) => ({data1: data1, data2: data2})
).subscribe(res => {
if(res.data1) {
doSomething1(res.data1)
}
if(res.data2) {
doSomething2(res.data2)
}
})
As @ritaj already mentioned, you seem to do it fine already, though:
startWith
with defaultIfEmpty operator to proceed only if any of the observables is empty. Heres a marble diagram to compare the two approaches:* Note that stream with startWith
emits twice
Use a unique object (or Symbol) instead of simple null
, just in case of a source stream really emits null
. This will ensure that we filter out only our markers, but not real results
Use a filter instead of if-else
in the subscription — should look just a bit cleaner. This is a general good practice to keep .subscribe
as thin as possible
Heres an example:
const NO_VALUE = {};
const result$ = combineLatest(
a$.pipe( defaultIfEmpty(NO_VALUE) ),
b$.pipe( defaultIfEmpty(NO_VALUE) )
)
.pipe(filter(([a, b]) => a !== NO_VALUE || b !== NO_VALUE))
^ Run this code with a marble diagram.
Hope this helps
You could use a BehaviorSubject. This provides you with a stream that emits a default value when subscribed to, if no more recent value is available (in this case null).
const subject1 = new BehaviorSubject(null),
subject2 = new BehaviorSubject(null);
source1.subscribe(subject1);
source2.subscribe(subject2);
combineLatest(subject1, subject2).subscribe(res => {
if(res.data1) {
doSomething1(res.data1)
}
if(res.data2) {
doSomething2(res.data2)
}
});
i know this an old thread. however, i recently faced the same issue and solved as @godblessstrawberry did
const source1 = new Subject(),
source2 = new Subject();
const vm$ = combineLatest([
source1.pipe(startWith([{id: 1}])),
source2.pipe(startWith(['abc'])),
])
.pipe(map(([profile, page]) => ({profile, page})));
vm$.subscribe(stream => {
console.log({stream});
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With