5

Does one need to unsubscribe from event based on control.

HTMLInput:

  const blur$ = fromEvent(formEle, 'blur').subscribe(x => {
    this.focused = false;
  });

FormGroup:

  const valChanges$ = this.fGroupRef.get('first_name').valueChanges.subscribe(val => {
  this.hasValue = !!val;
  });

Seeking a deeper understanding of what is going on here:

Both of these subscriptions are based upon the events of controls that will be destroyed when navigating away from the control. Will the subscription not unsubscribe as it will refer to nothing? ―OR― does the subscription itself create a reference to said control making it impossible to garbage collect?

Update forgot to mention: In my testing I have found that blur$ is undefined after component destruction, and will thrown an error if unsubscribed. However, valChanges$.unsubscribe() does not throw an error, yet they are both part of the same component.

  • Creating a subscription keeps it open in the framework as long as it isn't unsubscribed. Few exceptions exist, such as HTTP calls, which are finished once the request is done. As far as I know, events need to be unsubdcribed, because they are hot : they react to every event corresponding to the observable. –  Mar 25 '19 at 14:33

2 Answers2

2

If you read the source and docs of the FromEventObservable, you will find that it just adds / removes an event callback on the event target element. When the target element gets somehow destroyed before unsubcribing the observable, the callback will never be executed again. The observable itself, however, will still be technically 'subscribed' and wait for more events.

There is no mechanism in place, that observes the event targets 'existence' itself and unsubscribes the observable automatically for you.

FromEventObservable also tries to be as generic as possible. It does not expect your target to be a HTML DOM node.

The minimum expectation is, that your target has an addEventListener and a removeEventListener method. This implies it's not capable of tracking a theoretical 'Element got removed/destroyed Event'. FromEventObservable could in theory detect HTML nodes and give them a special treatment, but this would lead to inconsistent behavior between different target types.

TL;DR

Yes you should, because the RxJS dosn't know about removed event targets.

forgemo
  • 4,634
  • 5
  • 22
  • 25
  • I don't follow your reasoning. Given that FromEventObservable just adds an event listener to the element, the only thing that references the observer is the DOM element, and if the element is removed from the DOM, then nothing (should) reference it anymore, and it will thus be garbage-collected, with its listeners, and thus with the observable and its observers. So, unless the browser is buggy and causes a memory leak when you don't remove listeners before removing an element, you shouldn't have to unsubscribe. – JB Nizet Mar 25 '19 at 16:02
  • @JBNizet I tried to explain that staying subscribed wont do any real harm since there won't be any new events, but you still should unsubscribe to leave the 'subscribed' state. Whether instances get removed from memory or not is a different story. Explicitly unsubscribing will also tell other team members and your future self that you are finished with that observable. It also tells it to the RxJS Library. The library could use this to do smart things like cleaning up. Even if it doesn't do smart stuff right now, future versions might do. Rule of thumb: Use any given interface as intended. – forgemo Mar 26 '19 at 13:02
1

No, you do not need to unsubscribe if the target element is destroyed. If the element is destroyed then all event listeners are also destroyed as per If a DOM Element is removed, are its listeners also removed from memory?

Your event listener will not keep the element from being destroyed. If you're hanging on to the subscription (returned from subscribe) it will stick around until you no longer reference it. Once you stop referencing it then it too will be cleaned up.

If you have any complete subscriptions they will not be fired. The observable does not "complete" it simply does not fire any longer.

EDIT: Obligatory, if you don't unsubscribe yourself then you can't control when the last event may fire and that could be after your component is being cleaned up but before the element is actually removed and so your subscription could fire while your component is in an indeterminate state.

Pace
  • 41,875
  • 13
  • 113
  • 156