Oles Savluk
Oles Savluk

Reputation: 4345

Returning .empty Observable inside .switchMap operator

I think I've found a bug in the RxJS library or just can't understand it's behavior. Take a look at the following code:

const { Observable, Subject } = Rx;

const source$ = new Subject();

const handle = v => {
  if (v === 0) {
    source$.next(1);
    return Observable.empty();
  }  
  return Observable.interval(1000).startWith(-1).take(3);
}

source$
  .do(e => console.log('source:', e))
  // .delay(1)
  // .switchMap(v => Observable.of(null).switchMap(() => handle(v)))
  .switchMap(v => handle(v))
  .subscribe(e => console.log('handle:', e))

source$.next(0); // <- initial value
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

Working scenario: When I run it with source$.next(1), I see the following result(which is correct):

source: 1
handle: -1
handle: 0
handle: 1

Broken scenario: When I run it with source$.next(0), I expect to see:

source: 0
... (exactly the same output as for the previous example)

But instead I see only 3 values:

source: 0
source: 1
handle: -1

Workaround 1: When I put .delay(1) as you can see in the comments - it works fine, just as I have expected. I think this is because default Scheduler executed synchronously in the same tick vs using async Scheduler when using delay.

Question When I use Observable.of(null) (see comments in the code) it gives me the same("correct" or expected) result too. Why is that? Shouldn't it be the same synchronous as before?

Upvotes: 3

Views: 8194

Answers (1)

cartant
cartant

Reputation: 58400

The behaviour is correct. It's the synchronous emission from the source that makes it a little weird.

Emitting 1 from within the handle method effects a re-enterant call that sees the startWith value of -1 emitted before the Observable.empty is returned - as the startWith value is emitted synchronously.

That means that the switchMap sees the startWith/interval observable first and the empty observable second. It then switches to the empty observable, effecting the output that you see.

Adding a delay prevents the re-enterant call.

Upvotes: 4

Related Questions