How to know if switchMap unsubscribed an inner Observable?

Viewed 1195

Since switchMap may cut off (unsubscribe) an inner Observable if a new item comes in, we expect the following:

  const items = from([1, 2])
  const seen = []
  const derived = items.pipe(
    switchMap(item =>
      concat(
        of(`now: ${item}`),
        of(`end: ${item}`).pipe(delay(10))
      )
    )
  )

  derived.subscribe(next => seen.push(next))

to result in seen having:

['now: 1', 'now: 2', 'end: 2']
           ^ unsubscribed to 1 here, as planned!

The question is, is there a way to get notified that item 1 was canceled? The reason being, if cancellations are happening too frequently I'd like to know.

I've tried ways of modifying the inner Observables, but basically anything I try to do 'at the end' of them simply doesn't happen when unsubscribed. The Observer spec has next, completed, and error, but nothing for "ended early", so I'm not sure what to try.

I'd like to be able to pass an additional function to switchMap, which will be invoked by the operator when an inner observable is canceled, and provided the argument.

  const derived = items.pipe(
    switchMap(item => concat(
      of(`now: ${item}`),
      of(`end: ${item}`).pipe(delay(10))
    ),
    (item) => console.error(`${item} was cut off.`))
  )
1 Answers

Edit: There's an issue on RxJS GitHub page discussing this feature for the finalize operator. https://github.com/ReactiveX/rxjs/issues/2823

Since switchMap unsubscribes the inner Observable it won't receive any complete notification.

Instead you can just add a dispose handler or simply said use finalize() operator that does that for you and it's called when the chain is disposed. That's on both complete and unsubscribe events.

switchMap(item =>
  concat(
    of(`now: ${item}`),
    of(`end: ${item}`).pipe(delay(10))
  ).pipe(
    finalize(() => /* switchMap unsubscribed or concat completed */),
  )
)

Note, that when you unsubscribe it means you don't want to be receiving any more items from an Observable. This means that you can't receive any new items from the unsubscribed Observable when unsubscribing. There's for example defaultIfEmpty operator that emits an item when the source completes without emitting anything which works for complete notifications but can't work you're just unsubscribing.

Related