RxJS. Тонкость работы iif

iif принимает функцию-условие и 2 необязательных Observable. Роль iif заключается в том, чтобы подписаться на один из Observable. В простых случаях все работает ожидаемо:

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

const source$ = of('Hello');

source$.pipe(
  mergeMap(v =>
    iif(
      () => v === 'Hello',
      of('first').pipe(tap(x => console.log(`>>> ${x} <<<`))),
      of('second').pipe(tap(x => console.log(`>>> ${x} <<<`))),
    ))
).subscribe(console.log);
// >>> first <<<
// first

Проблема может возникнуть, если использовать оператор для задачи отправки http-запросов (2 и 3 аргумент iif) в зависимости от результата функции-условия т.е. если вернулось true, то сделать первый запрос, если false - второй.

iif(
  () => Boolean(condition),
  this.httpRequestOne(),
  this.httpRequestTwo()
)

В действительности будут выполнены оба запроса, но в поток вернётся результат только одного. Такое поведение можно проиллюстрировать следующим примером:

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);

Будет выведено

Hello World
Hello, Goodbye # ВНЕЗАПНО
One

Решением будет использовать оператор defer вместо iif.

defer(() => (
  Boolean(condition) 
    ? this.httpRequestOne()
    : this.httpRequestTwo()
))

Похожие записи

RxJs Subjects

Выдержки из доклада Андрея Алексеева (Tinkoff) про RxJs (Subject, Behaviour Subject, Replay Subject, Async Subject). Применение в Angular.