Reputation: 759
I have 2 observables and I want to get a value from X if it exists, else get it from Y. The following works for me, but I am looking for a more succinct pattern.
import {of} from 'rxjs';
import {map, mergeMap} from 'rxjs/operators';
import {getX, getY} from './getXY';
// getX(id: string): Observable<X>; // gets an X asyncronously
// getY(id: string): Observable<Y>; // gets a Y asyncronously
let id = "abc";
// get an X, or a Y if not X, that matches the id
getX(id).pipe(mergeMap((x: X) => {
if (x) {
return of(x);
} else {
return getY(id).pipe(map((y: Y) => {
if (y) {
return y;
}
}));
}
})).subscribe((xy: X | Y) => { ... }
It seems to me that the of(x) is going in the wrong direction - that perhaps instead of mergeMap, there is a way to conditionally extract the Y from the getY without the need for the of(x).
Ideally I will write a getXY that does all that and returns an Observable<X | Y> that can be subscribed to.
I create a codepen with the original example and cartant's answer. Notice that when getX returns 'x', cartant's still calls getY - that is why I did not mark his answer as the solution. Feel free to fork and add your own solution.
Upvotes: 2
Views: 277
Reputation: 759
The simplest solution with operators, that doesn't call getY when getX succeeds is:
function getXY(id: string): Observable<X|Y> {
return getX(id).pipe(mergeMap((x: X) => {
if (x) {
return of(x);
} else {
return getY(id);
}
}));
};
I have updated the codepen with this getXY function included. As well as getXYcartant - and the following getXYnew:
Observable.create
is another possible answer since it doesn't call getY if X is null:
function getXYnew(id: string): Observable<X|Y> {
return Observable.create(observer => {
getX(id).subscribe(x => {
if (x) {
observer.next(x);
observer.complete();
} else {
getY(id).subscribe(y => {
observer.next(y);
observer.complete();
});
}
});
});
};
But this, like the other more performance optimal options, creates another Observable.
Upvotes: 0
Reputation: 58410
You could implement your getXY
function like this:
function getXY(id: string /* or whatever */) {
return concat(
getX(id).pipe(filter(x => Boolean(x))),
getY(id).pipe(filter(y => Boolean(y)))
).pipe(take(1));
}
The function's return type will be inferred to be Observable<X | Y>
and the implementation is a little less imperative.
Using concat
will ensure that the observable returned by getY
will only be subscribed to if the observable returned by getX
either completes without emitting or emits an falsy value.
Upvotes: 4