RxJS iif arguments are called when shouldn't

A little late to the party, but I found that the role of iif is not to execute one path over the other, but to subscribe to one Observable or the other. That said, it will execute any and all code paths required to get each Observable.

From this example...

import { iif, of, pipe } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

const source$ = of('Hello');
const obsOne$ = (x) => {console.log(`${x} World`); return of('One')};
const obsTwo$ = (x) => {console.log(`${x}, Goodbye`); return of('Two')};

source$.pipe(
  mergeMap(v =>
    iif(
      () => v === 'Hello',
      obsOne$(v),
      obsTwo$(v)
    ))
).subscribe(console.log);

you'll get the following output

Hello World
Hello, Goodbye
One

This is because, in order to get obsOne$ it needed to print Hello World. The same is true for obsTwo$ (except that path prints Hello, Goodbye).

However you'll notice that it only prints One and not Two. This is because iif evaluated to true, thus subscribing to obsOne$.

While your ternary works - I found this article explains a more RxJS driven way of achieving your desired outcome quite nicely: https://rangle.io/blog/rxjs-where-is-the-if-else-operator/


Ok, i found an answer on my own. My solution is to remove iif completely and rely on just ternary operator inside mergeMap. that way its not evaluated after every 'LOCATION_CHANGE' and just if regExp returns true. Thanks for your interest.

export const roomRouteEpic: Epic = (action$, state$) =>
  action$.ofType(LOCATION_CHANGE).pipe(
    pluck<any, any>('payload'),
    mergeMap(payload =>
      /^\/room\/\d+$/.test(payload.location.pathname)
        ? of(
            state$.value.rooms.list[payload.location.pathname.split('/')[2]]
              ? actions.rooms.initRoomEnter()
              : actions.rooms.initRoomCreate(),
            actions.global.setIsLoading(true),
          )
        : EMPTY,
    ),
  );