In a few places I have needed to call setTimeout, eg:
setTimeout(() => this.isSaving[index] = false, 500);
When my component is destroyed, will that timeout continue to emit? In other words do I need to capture the returned observable, like this:
this.subTimeout = setTimeout(() => this.isSaving[index] = false, 500);
and then unsub in my destroy hook:
ngOnDestroy() {
this.subTimeout.unsubscribe();
}
This gets laborious if I have to initiate several setTimeouts in my component. Is there an easier way to destroy them all? Like maybe with takeUntil(destroy$)?
In a few places I have needed to call setTimeout, eg:
setTimeout(() => this.isSaving[index] = false, 500);
When my component is destroyed, will that timeout continue to emit? In other words do I need to capture the returned observable, like this:
this.subTimeout = setTimeout(() => this.isSaving[index] = false, 500);
and then unsub in my destroy hook:
ngOnDestroy() {
this.subTimeout.unsubscribe();
}
This gets laborious if I have to initiate several setTimeouts in my component. Is there an easier way to destroy them all? Like maybe with takeUntil(destroy$)?
Share Improve this question asked Oct 24, 2017 at 13:45 rmcsharryrmcsharry 5,55211 gold badges72 silver badges114 bronze badges 5 |2 Answers
Reset to default 17do I need to unsubscribe from setTimeout calls?...When my component is destroyed, will that timeout continue to emit?
You don't need to worry about the callback executing repeatedly. setTimeout
executes just once then it's dead. It's generally good practice to account for the possibility that the time will run out and the callback execute only after the component has been destroyed though. In my own applications, I throw in an array all subscriptions that need to be undone, and I have a standard batch unsubscribe job in ngOnDestroy
. The same can be done with your timeouts:
// component property
timeOutIDs:number[] = [];
...
// triggering a timeout and capturing the id
this.timeOutIDs.push(
setTimeout(() => this.isSaving[index] = false, 500)
);
...
// inside ngOnDestroy
this.timeoutIDs.forEach(id => clearTimeout(id));
With this approach you won't need multiple variables to store different timeout ids, and you can be sure that all your timeouts will be cleared properly if you always push
the return value of setTimeout
in your ids array.
Additional note: You should always cancel setInterval
calls and always unsubscribe from open-ended subscriptions though.
It depends on what's going on inside setTimeout
callbacks, but generally they should be unsubscribed. There's no way how they could be magically be unsubscribed. Their callbacks will be fired any way and may cause errors or undesirable side effects.
It is a good practice to make timeout be assigned somewhere, at least for the purpose of testing. The thing that is supposed to be done in ngOnDestroy
, this.subTimeout.unsubscribe()
presumes that a timeout is performed through RxJS:
this.subTimeout = Observable.timer(500).subscribe(() => {
this.isSaving[index] = false;
});
The way it can be improved depends on what purpose these timeouts serve.
Observable.timer(1000)
instead ofsetTimeout
if you want it to be observable. As to it overcomplicating it, I use the base class version and it simplifies the destruction since I don't have to track subscriptions or deal with null checks if they were created in ngOnInit. It also allows @Inputs to be observable via onChanges. – bygrace Commented Oct 25, 2017 at 16:18