diff --git a/ably.d.ts b/ably.d.ts index 4f7c82fb5..c80717665 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -2718,7 +2718,7 @@ export declare interface Channels { */ getDerived(name: string, deriveOptions: DeriveOptions, channelOptions?: ChannelOptions): T; /** - * Releases a {@link Channel} or {@link RealtimeChannel} object, deleting it, and enabling it to be garbage collected. To release a channel, the {@link ChannelState} must be `INITIALIZED`, `DETACHED`, or `FAILED`. + * Releases all SDK-held references to a {@link Channel} or {@link RealtimeChannel} object, enabling it to be garbage collected. Warning: this method has no guardrails; using a channel reference after it has been released is undefined behaviour. It can be useful for applications that work with a continually changing set of channels on a single client and need to avoid unbounded memory growth; if this does not describe you, don't call it. Realtime channels not already in the `INITIALIZED`, `DETACHED`, or `FAILED` state are detached before release. * * @param name - The channel name. */ diff --git a/src/common/lib/client/baserealtime.ts b/src/common/lib/client/baserealtime.ts index 73a5411fb..c32251397 100644 --- a/src/common/lib/client/baserealtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -223,15 +223,29 @@ class Channels extends EventEmitter { * Please do not use this unless you know what you're doing */ release(name: string) { name = String(name); + Logger.logAction(this.logger, Logger.LOG_MAJOR, 'Channels.release()', 'Releasing references to channel ' + name); const channel = this.all[name]; if (!channel) { return; } - const releaseErr = channel.getReleaseErr(); - if (releaseErr) { - throw releaseErr; + const s = channel.state; + if (s === 'initialized' || s === 'detached' || s === 'failed') { + delete this.all[name]; + return; } - delete this.all[name]; + channel + .detach() + .catch((err) => { + Logger.logAction( + this.logger, + Logger.LOG_ERROR, + 'Channels.release()', + 'Error detaching channel ' + name + ' prior to release: ' + Utils.inspectError(err), + ); + }) + .then(() => { + delete this.all[name]; + }); } }