diff --git a/package-lock.json b/package-lock.json index f7105e078..adc39c456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5350,9 +5350,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001538", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", - "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", + "version": "1.0.30001611", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz", + "integrity": "sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==", "dev": true, "funding": [ { @@ -21127,9 +21127,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001538", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", - "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", + "version": "1.0.30001611", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz", + "integrity": "sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==", "dev": true }, "chai": { diff --git a/src/lib/PingTimer.ts b/src/lib/PingTimer.ts index e5c7b9f09..be18f45d9 100644 --- a/src/lib/PingTimer.ts +++ b/src/lib/PingTimer.ts @@ -10,6 +10,8 @@ export default class PingTimer { private checkPing: () => void + private destroyed = false + constructor( keepalive: number, checkPing: () => void, @@ -21,22 +23,34 @@ export default class PingTimer { this.reschedule() } - clear() { + private clear() { if (this.timerId) { this.timer.clear(this.timerId) this.timerId = null } } + destroy() { + this.clear() + this.destroyed = true + } + reschedule() { + if (this.destroyed) { + return + } + this.clear() this.timerId = this.timer.set(() => { - this.checkPing() - // prevent possible race condition where the timer is destroyed on _cleauUp - // and recreated here - if (this.timerId) { - this.reschedule() + // this should never happen, but just in case + if (this.destroyed) { + return } + + this.checkPing() + // this must be called after `checkPing` otherwise in case `destroy` + // is called in `checkPing` the timer would be rescheduled anyway + this.reschedule() }, this.keepalive) } } diff --git a/src/lib/client.ts b/src/lib/client.ts index d3c108a0a..5c73bb3d5 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -659,9 +659,9 @@ export default class MqttClient extends TypedEventEmitter { clock.restore() }) - it('should schedule and clear', () => { + it('should schedule and destroy', () => { const keepalive = 10 // seconds const cb = spy() const pingTimer = new PingTimer(keepalive, cb, 'auto') @@ -28,10 +28,15 @@ describe('PingTimer', () => { ) clock.tick(keepalive * 1000 + 1) assert.equal(cb.callCount, 2, 'should reschedule automatically') - pingTimer.clear() + pingTimer.destroy() assert.ok( !pingTimer['timerId'], - 'timer should not exists after clear()', + 'timer should not exists after destroy()', + ) + + assert.ok( + pingTimer['destroyed'], + 'timer should have `destroyed` set to true after destroy()', ) }) @@ -41,7 +46,7 @@ describe('PingTimer', () => { const pingTimer = new PingTimer( keepalive, () => { - pingTimer.clear() + pingTimer.destroy() cb() }, 'auto',