Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is completing the Subject necessary when using the takeUntil pattern to unsubscribe from Observables?

To avoid memory leaks in my Angular app i'm using the following well-known pattern to unsubscribe from Observables:

unsubscribe = new Subject();

ngOnInit() {
    this.myService.getStuff()
        .pipe(takeUntil(this.unsubscribe))
        .subscribe(result => {
            // processing the result
        });
}

ngOnDestroy() {
    this.unsubscribe.next();
}

This seemingly works fine, but in some examples i've noticed that complete() is also called on the Subject in addition to next():

ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete(); // like this
}

Is calling complete() necessary here? If so, why? What are the consequences of not calling complete() in this scenario?

like image 214
justanoob Avatar asked Feb 02 '19 10:02

justanoob


People also ask

Does TakeUntil complete the observable?

The takeUntil operator returns an Observable that emits value from the source Observable until the notifier Observable emits a value. When the notifier emits a value, the TakeUntil completes the Source observable.

Do I need to unsubscribe from a completed observable?

A fundamental aspect of observables is that when they complete, any subscriptions are automatically unsubscribed. As such, if you know an observable will complete then you do not need to worry about cleaning up any subscriptions.

How do I unsubscribe from TakeUntil?

We can simply call the unsubscribe() method from the Subscription object returned by the subscribe() method in the ngOnDestroy() life-cycle method of the component to unsubscribe from the Observable. There is also a better way to unsubscribe from or complete Observables by using the takeUntil() operator.

Does TakeUntil subscribe?

takeUntil subscribes and begins mirroring the source Observable. It also monitors a second Observable, notifier that you provide. If the notifier emits a value, the output Observable stops mirroring the source Observable and completes.


1 Answers

Let's see why you need to unsubscribe first.

Very simplified: Observable instance is holding an array of all subscriptions, which means every callback you have in your subscribe will be held in this array. This is bad news for Component because while it is referred from those functions it cannot be garbage-collected. I talk about these functions:

ngOnInit() {
    this.myService.getStuff()
        .subscribe(
            result => null, // this function will be stored in Observable
            error => null, // and this
            () => null, // and even this
        );
}

and it is applicable to every subscribe call.

Now you add a pipe .pipe(takeUntil(this.unsubscribe)) (or you can e.g. use my small library that does similar but shorter). In fact, your Observable subscribes to the events of Subject. And, whenever it emits a value, the Observable returned by this.myService.getStuff() will complete itself. That means all three functions above will be removed from this Observable's subscriptions array and your component is not referred from there anymore.

Problem solved.

All above you need to understand all the whys you have.

Here we finally come to your question

ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
}

where complete is unnecessary, but not harming as well. Because the only subscriber to this subject was your Observable from this.myService.getStuff() (or other Observables from the same component). That means this Subject will refer to nothing else (the only listener is removed and complete that is supposed to clear all subscriptions is already empty), and as long as only component has reference to the Subject as its property, they both will be collected by garbage collector.

like image 67
smnbbrv Avatar answered Oct 04 '22 22:10

smnbbrv