Managing subscriptions

This is more of a generic RxJS concept than an Ionic-specific one, but hopefully will be of use to somebody here.

The bulk of most people’s interactions with Observables is probably retrieving Http requests, which is a one-shot operation and basically a glorified Promise. However, there are some situations where you have an Observable that outlasts a particular subscription. Typically most people probably do something along these lines:

ionVievWillEnter() {
  this.sub = this.service.obserableThingy().subscribe(thingy => this.frobulate(thingy));
}

ionViewWillLeave() {
  this.sub.unsubscribe();
}

There’s nothing intrinsically bad about this approach, but I recently encountered a situation in which it didn’t scale.

Imagine a long-lived, reusable dialog (not Ionic’s modals, which aren’t designed that way). It has two output properties: proceed and cancel, and a show() method. When either proceed or cancel fires, the dialog is now known to be closed.

If we subscribe only to proceed, that means we may never know that the dialog got cancelled, and therefore we don’t know when to unsubscribe from proceed. We don’t want to get a spurious notification when another code path has reopened the dialog for a different purpose.

We could subscribe to both, and then unsubscribe both of those when either fires, but that gets us into a bit of a gnarly situation:

There is a more elegant and scaleable approach, using what is (to me, at least) a relatively obscure operator: takeUntil. takeUntil allows a separate Observable to act as a tripwire for subscriptions, allowing a bunch of them to be reaped at once without the need to track any of them and explicitly unsubscribe.

So we can do this:

let unsub = new Subject<void>();
this.dlog.proceed.takeUntil(unsub).subscribe(() => {
  doSomething());
  unsub.next();
});
this.dlog.cancel.takeUntil(unsub).subscribe(() => {
  unsub.next();
});

The same strategy will work to cut off any arbitrary number of subscriptions to related events.

4 Likes