I have a check which needs to combine 2 checks:
Is there a more elegant way of wrapping a value in an observable only if not observable already? Or is there a way to combine the first check with the second when the second is Observable? Or still refactor all this in a more efficent way?
Of course this code is an example to simulate what I get in the real app (see comments in code).
const getRandomBool: () => boolean = () => Math.random() >= 0.5;
const getRandomCheck: () => boolean | Observable<boolean> = () => {
return getRandomBool() ? getAsyncCheck() : getSyncCheck();
};
const myFunction = (): Observable<boolean> | boolean => {
// First check telling if user is logged, which the UserService returns as Observable<boolean>
return of(getRandomBool())
.pipe(
mergeMap((isLogged) => {
// This is the check which can be either boolean or Observable<boolean>
const myCheck = getRandomCheck();
return isLogged
? iif(() => typeof myCheck === 'boolean', of(myCheck), myCheck)
: of(!1)
})
)
};
``
What about checking first if "myCheck" was Observable and use forkJoin?
const myFunction: () => Observable<boolean> = () => {
let myCheck = getRandomCheck();
myCheck = isObservable(myCheck) ? myCheck : of(myCheck);
return forkJoin([of(getRandomBool()), myCheck])
.pipe(
map(([logged, yourCheck]) => {
return logged ? yourCheck : !1;
})
)
}
EDIT
As suggested by BizzyBob, in case myCheck is async and has a huge span it could pointlessly affect the whole execution.
So we can use switchMap to avoid the issue
const myFunction: () => Observable<boolean> = () => {
let myCheck = getRandomCheck();
myCheck = isObservable(myCheck) ? myCheck : of(myCheck);
return of(getRandomBool())
.pipe(
switchMap((logged: boolean) => {
return logged ? myCheck : of(!1);
})
)
}
Example
const { of, iif, pipe, isObservable , operators: { mergeMap } } = rxjs;
const getAsyncCheck = () => of(Boolean(Math.floor(Math.random() * 2)));
const getSyncCheck = () => Boolean(Math.floor(Math.random() * 2));
const getRandomBool = () => Boolean(Math.floor(Math.random() * 2));
const getRandomCheck = () => {
return getRandomBool() ? getAsyncCheck() : getSyncCheck();
};
const myFunction = () => {
return of(getRandomBool())
.pipe(
mergeMap((isLogged) => {
const myCheck = getRandomCheck();
const result = isLogged ? iif(() => isObservable(myCheck), myCheck, of(myCheck)) : of(false);
return result;
})
).subscribe(result => {
console.log(result)
})
};
myFunction();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
I'd coerce randomCheck to observable, then use exhaustMap to handle returning the proper observable; either of(false) when, isLogged = false or randomCheck$ otherwise:
const myFunction = (): Observable<boolean> => {
const randomCheck$ = isObservable(randomCheck) ? randomCheck : of(randomCheck);
return getIsLogged().pipe(
exhaustMap(isLogged => isLogged ? randomCheck$ : of(false)),
// take(1)
);
};
If getIsLogged() doesn't complete, I think you need to use take(1) for angular guard.