简体   繁体   中英

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?

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 eg 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 why s 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.

This has been discussed previously eg. here Why a 'next' before 'complete' of Angular takeuntil ngUnsubscribe?

You basically don't have to call complete() because next() will dispose the chain and takeUntil will unsubscribe from this.unsubscribe for you. Only if you had some other logic tied to this.unsubscribe then it might be necessary to call complete() .

Anyway, you don't break anything if you do call complete() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM