Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit b4c4d0c

Browse files
authored
fix: stop push timers on disconnect (#469)
1 parent 14de716 commit b4c4d0c

File tree

3 files changed

+23
-13
lines changed

3 files changed

+23
-13
lines changed

src/RealtimeChannel.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,6 @@ export default class RealtimeChannel {
514514
this._trigger(CHANNEL_EVENTS.close, 'leave', this._joinRef())
515515
}
516516

517-
this.rejoinTimer.reset()
518-
// Destroy joinPush to avoid connection timeouts during unscription phase
519517
this.joinPush.destroy()
520518

521519
return new Promise((resolve) => {
@@ -539,6 +537,16 @@ export default class RealtimeChannel {
539537
}
540538
})
541539
}
540+
/**
541+
* Teardown the channel.
542+
*
543+
* Destroys and stops related timers.
544+
*/
545+
teardown() {
546+
this.pushBuffer.forEach((push: Push) => push.destroy())
547+
this.rejoinTimer && clearTimeout(this.rejoinTimer.timer)
548+
this.joinPush.destroy()
549+
}
542550

543551
/** @internal */
544552

src/RealtimeClient.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Timer from './lib/timer'
1717
import { httpEndpointURL } from './lib/transformers'
1818
import RealtimeChannel from './RealtimeChannel'
1919
import type { RealtimeChannelOptions } from './RealtimeChannel'
20+
import Push from './lib/push'
2021

2122
type Fetch = typeof fetch
2223

@@ -252,9 +253,11 @@ export default class RealtimeClient {
252253
this.conn.close()
253254
}
254255
this.conn = null
256+
255257
// remove open handles
256258
this.heartbeatTimer && clearInterval(this.heartbeatTimer)
257259
this.reconnectTimer.reset()
260+
this.channels.forEach((channel) => channel.teardown())
258261
}
259262
}
260263

@@ -274,6 +277,7 @@ export default class RealtimeClient {
274277
): Promise<RealtimeRemoveChannelResponse> {
275278
const status = await channel.unsubscribe()
276279
this.channels = this.channels.filter((c) => c._joinRef !== channel._joinRef)
280+
277281
if (this.channels.length === 0) {
278282
this.disconnect()
279283
}
@@ -288,8 +292,8 @@ export default class RealtimeClient {
288292
const values_1 = await Promise.all(
289293
this.channels.map((channel) => channel.unsubscribe())
290294
)
291-
this.disconnect()
292295
this.channels = []
296+
this.disconnect()
293297
return values_1
294298
}
295299

test/channel.test.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,15 @@ describe('joinPush', () => {
414414
}
415415

416416
beforeEach(() => {
417+
socket.disconnect()
418+
channel.unsubscribe()
419+
417420
channel = socket.channel('topic')
418421
joinPush = channel.joinPush
419422

420423
channel.subscribe()
421424
})
422425

423-
afterEach(() => {
424-
socket.disconnect()
425-
channel.unsubscribe()
426-
})
427-
428426
describe("receives 'ok'", () => {
429427
beforeEach(() => {
430428
response = { chan: 'reply' }
@@ -1094,6 +1092,8 @@ describe('leave', () => {
10941092
let socketSpy
10951093

10961094
beforeEach(() => {
1095+
socket.disconnect()
1096+
channel.unsubscribe()
10971097
socket = new RealtimeClient('ws://example.com/socket', {
10981098
timeout: defaultTimeout,
10991099
})
@@ -1105,11 +1105,6 @@ describe('leave', () => {
11051105
channel.joinPush.trigger('ok', {})
11061106
})
11071107

1108-
afterEach(() => {
1109-
socket.disconnect()
1110-
channel.unsubscribe()
1111-
})
1112-
11131108
test('unsubscribes from server events', () => {
11141109
sinon.stub(socket, '_makeRef').callsFake(() => defaultRef)
11151110

@@ -1124,6 +1119,9 @@ describe('leave', () => {
11241119
join_ref: defaultRef,
11251120
})
11261121
)
1122+
1123+
assert.equal(channel.state, CHANNEL_STATES.closed)
1124+
assert.deepEqual(channel.pushBuffer, [])
11271125
})
11281126

11291127
test("closes channel on 'ok' from server", () => {

0 commit comments

Comments
 (0)