Reputation: 1807
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?
Upvotes: 6
Views: 1173
Reputation: 24531
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 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.
Upvotes: 4
Reputation: 96891
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()
.
Upvotes: 1