Sean Walsh
Sean Walsh

Reputation: 8344

Do I need to complete a Subject for it to be garbage collected?

I follow a cleanup pattern in my Angular components that looks like this:

class SomeComponent implements OnInit, OnDestroy {
    private destroy$ = new Subject();

    ngOnInit() {
        service.someStream().takeUntil(this.destroy$).subscribe(doSomething);
    }

    ngOnDestroy() {
        this.destroy$.next(true);
    }
}

This has the benefit of automatically unsubscribing when the component is destroyed.

My question is: Does a reference to destroy$ stick around indefinitely because I haven't called this.destroy$.complete(), or will it get GC'ed when the parent class is collected?

Upvotes: 33

Views: 17277

Answers (2)

jadc
jadc

Reputation: 362

The original post actually poses two questions:

  • one in the title "Do I need to complete a Subject for it to be garbage collected?"

The answer to that question is "Yes, sometimes". So normally you should do a complete unless you know for sure it is not necessary. That is because any call to subscribe will return a Subscription object which does refer back to the observable. See, for example, https://www.learnrxjs.io/learn-rxjs/concepts/rxjs-primer#subscription.

(The statement in the answer If you look at the source ... you'll find the answer: "Subscribers don't hold references to the observables..." is misleading. Maybe the subscriber itself does not hold a reference to the Observable - but the returned Subscription object does.)

This reference can stop the Subject being garbage collected - unless complete is called to remove the subscription, or it is removed in some other way. Which leads to the second question in the post...

  • the question implicit in the example: "Do I need to call complete on the subject in this example?".

In this particular example it is not necessary to call complete because the subscription to the subject, "destroy$", will have been created - and removed - by the takeUntil call. So if the OP wants to know whether a call to complete is needed in that particular example, the answer is no.

Upvotes: 0

cartant
cartant

Reputation: 58400

If you look at the source for Subject.complete, you'll find the answer:

complete() {
  if (this.closed) {
    throw new ObjectUnsubscribedError();
  }
  this.isStopped = true;
  const { observers } = this;
  const len = observers.length;
  const copy = observers.slice();
  for (let i = 0; i < len; i++) {
    copy[i].complete();
  }
  this.observers.length = 0;
}

Calling complete notifies any observers and then clears the array of observers. Unless you have an observer/subscriber that has a reference to the Subject, there is nothing in the complete implementation that would affect whether or not the Subject could be garbage collected.

RxJS pushes notifications to subscribers. Subscribers don't hold references to the observables; it's the other way around. So, unless you've explicitly created a subscriber that holds a reference to the Subject - via a closure or some other mechanism - there's no need to call complete for garbage-collection purposes.

Upvotes: 38

Related Questions