Reputation: 241
I have made a class Service. I am trying to run a Interval every X seconds. The default is 30 seconds. But I want to set a custom delay if needed in other classes. I can't find a way to override the variables in my parent class.
class Service {
delay = 30;
constructor(api, client = undefined) {
this.api = api;
this.client = client;
this.handle();
}
getMiliSeconds() {
return this.delay * 1000;
}
service = async () => {
// Service to run in background
}
handle() {
setInterval(() => { this.service() }, this.getMiliSeconds());
}
}
module.exports = Service;
When I extend the class Service I am trying to override the delay
variable
class Notification extends Service {
delay = 60;
service = async () => {
// I am not running every 60 seconds.
}
}
module.exports.Notification = Notification;
However the Interval function is still runned every 30 seconds. Instead of the 60 seconds I set in the Notifications class.
Upvotes: 1
Views: 4118
Reputation: 370699
The problem is that super constructors run before child constructors run or assign to instances. For example:
class Parent {
prop = 'foo'
constructor() {
console.log(this.prop);
}
}
class Child extends Parent {
prop = 'bar'
}
will always log foo
, because the class field prop = 'bar'
will only run after the parent constructor finishes and control flow is yielded back to the child.
When you call this.handle
in the super constructor, getMiliSeconds
runs immediately, retrieving the delay before the child has had a chance to assign its own delay
to the instance.
To fix it, I'd move this.handle
out of the constructor, so that it can be called on demand after the object is fully instantiated, not before.
In the snippet below, the delay = 1
in the child class is now successfully causing the child service to run every second, instead of every 3 seconds.
class Service {
delay = 3;
constructor(api, client = undefined) {
this.api = api;
this.client = client;
}
getMiliSeconds() {
return this.delay * 1000;
}
service = async () => {
console.log('parent service');
// Service to run in background
}
handle() {
setInterval(() => { this.service() }, this.getMiliSeconds());
}
}
class Notification extends Service {
delay = 1;
service = async () => {
console.log('child service');
}
}
const n = new Notification();
n.handle();
Another approach would be to put the delay
property on the prototype, instead of as a class field (which is equivalent to inside the constructor - and the time that the relative constructors are permitted to run is the problem):
class Service {
constructor(api, client = undefined) {
this.api = api;
this.client = client;
this.handle();
}
getMiliSeconds() {
return this.delay * 1000;
}
service = async () => {
console.log('parent service');
// Service to run in background
}
handle() {
setInterval(() => { this.service() }, this.getMiliSeconds());
}
}
Service.prototype.delay = 3;
class Notification extends Service {
service = async () => {
console.log('child service');
}
}
Notification.prototype.delay = 1;
const n = new Notification();
Upvotes: 8