1
votes

I have the following code:

obs.pipe(
   switchMap((val) => {
       // some logic...
       return iif(() => condition, someObservable())
   })
);

I am just wondering why couldnt this have been achieved with a simple if instead of the iif?

 if (condition) {
    return someObservable();
 }
2
In the first case, if the condition is false the resulting stream completes immediately. In the second case, it appears you are returning no stream if the condition is false. To be comparable, the second example would need to have an else and return EMPTY. The doc for iif is here: rxjs.dev/api/index/function/iif " - DeborahK

2 Answers

2
votes

Take a look at the implementation of iif:

export function iif<T = never, F = never>(
  condition: () => boolean,
  trueResult: SubscribableOrPromise<T> = EMPTY,
  falseResult: SubscribableOrPromise<F> = EMPTY
): Observable<T|F> {
  return defer(() => condition() ? trueResult : falseResult);
}

iif is using defer to only call the condition function and pick an (inner) Observable when the (outer) Observable is subscribed to. This doesn't really make a difference when used inside switchMap because the code inside switchMap is executed on every emit, but results in different behaviour when you use iif to create an Observable and subscribe to it at a later time.

const { iif, of, concat } = rxjs;

let subscribeToFirst;
const obs$ = concat(
  iif(() => subscribeToFirst, of('iif-first'), of('iif-second')), // use 'iif'
  subscribeToFirst ? of('if-first (never logged)') : of('if-second') // use 'if'
);

// Change at runtime which Observable will be subscribed
// works for 'iif' but not for 'if'
console.log('----- subscribe to first -----');
subscribeToFirst = true;
obs$.subscribe(value => console.log(value));
console.log('----- subscribe to second -----');
subscribeToFirst = false;
obs$.subscribe(value => console.log(value));
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

Apart from that the documentation also says that iif exists for convenience:

Actually iif can be easily implemented with defer and exists only for convenience and readability reasons.

0
votes

As mentioned in the comments:

In the first case, if the condition is false the resulting stream completes immediately. In the second case, it appears you are returning no stream if the condition is false. To be comparable, the second example would need to have an else and return EMPTY. The doc for iif is here: rxjs.dev/api/index/function/iif

Using both the true and false condition, the statements would be comparable:

  getUser() {
    const url = `${this.userUrl}/${this.currentUserId}`;
    this.todosForUser$ = this.http.get<User[]>(url).pipe(
      switchMap(user => {
        return iif(
          () => this.todos,
          this.http.get<ToDo[]>(`${this.todoUrl}?userId=${this.currentUserId}`),
          this.http.get<Post[]>(`${this.postUrl}?userId=${this.currentUserId}`)
        );
      })
    );
  }

And

  getUser() {
    const url = `${this.userUrl}/${this.currentUserId}`;
    this.todosForUser$ = this.http.get<User[]>(url).pipe(
      switchMap(user => {
        if (this.todos) {
          return this.http.get<ToDo[]>(`${this.todoUrl}?userId=${this.currentUserId}`);
        } else {
          return this.http.get<Post[]>(`${this.postUrl}?userId=${this.currentUserId}`);
        }
      })
    );
  }

You can find the stackblitz here: https://stackblitz.com/edit/angular-todos-deborahk-iif

So it comes down to which you and your team think is the easiest to read.