From 5ca97c93515d4dfaa2b4951a020abc000115ed4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Rom=C3=A1n?= Date: Fri, 16 Jul 2021 14:31:25 +0200 Subject: [PATCH] refactor: remove timer utilities from Client (#6113) --- src/client/BaseClient.js | 97 +----------------------- src/client/Client.js | 7 +- src/client/actions/GuildDelete.js | 2 +- src/client/websocket/WebSocketManager.js | 4 +- src/client/websocket/WebSocketShard.js | 24 +++--- src/managers/GuildManager.js | 6 +- src/managers/GuildMemberManager.js | 6 +- src/rest/APIRequest.js | 4 +- src/rest/RESTManager.js | 5 +- src/rest/RequestHandler.js | 4 +- src/structures/GuildTemplate.js | 4 +- src/structures/interfaces/Collector.js | 20 ++--- typings/index.d.ts | 9 --- 13 files changed, 47 insertions(+), 145 deletions(-) diff --git a/src/client/BaseClient.js b/src/client/BaseClient.js index f501b3a9d..65dd18470 100644 --- a/src/client/BaseClient.js +++ b/src/client/BaseClient.js @@ -13,27 +13,6 @@ class BaseClient extends EventEmitter { constructor(options = {}) { super(); - /** - * Timeouts set by {@link BaseClient#setTimeout} that are still active - * @type {Set} - * @private - */ - this._timeouts = new Set(); - - /** - * Intervals set by {@link BaseClient#setInterval} that are still active - * @type {Set} - * @private - */ - this._intervals = new Set(); - - /** - * Intervals set by {@link BaseClient#setImmediate} that are still active - * @type {Set} - * @private - */ - this._immediates = new Set(); - /** * The options the client was instantiated with * @type {ClientOptions} @@ -60,82 +39,10 @@ class BaseClient extends EventEmitter { /** * Destroys all assets used by the base client. + * @returns {void} */ destroy() { - for (const t of this._timeouts) this.clearTimeout(t); - for (const i of this._intervals) this.clearInterval(i); - for (const i of this._immediates) this.clearImmediate(i); - this._timeouts.clear(); - this._intervals.clear(); - this._immediates.clear(); - } - - /** - * Sets a timeout that will be automatically cancelled if the client is destroyed. - * @param {Function} fn Function to execute - * @param {number} delay Time to wait before executing (in milliseconds) - * @param {...*} args Arguments for the function - * @returns {Timeout} - */ - setTimeout(fn, delay, ...args) { - const timeout = setTimeout(() => { - fn(...args); - this._timeouts.delete(timeout); - }, delay); - this._timeouts.add(timeout); - return timeout; - } - - /** - * Clears a timeout. - * @param {Timeout} timeout Timeout to cancel - */ - clearTimeout(timeout) { - clearTimeout(timeout); - this._timeouts.delete(timeout); - } - - /** - * Sets an interval that will be automatically cancelled if the client is destroyed. - * @param {Function} fn Function to execute - * @param {number} delay Time to wait between executions (in milliseconds) - * @param {...*} args Arguments for the function - * @returns {Timeout} - */ - setInterval(fn, delay, ...args) { - const interval = setInterval(fn, delay, ...args); - this._intervals.add(interval); - return interval; - } - - /** - * Clears an interval. - * @param {Timeout} interval Interval to cancel - */ - clearInterval(interval) { - clearInterval(interval); - this._intervals.delete(interval); - } - - /** - * Sets an immediate that will be automatically cancelled if the client is destroyed. - * @param {Function} fn Function to execute - * @param {...*} args Arguments for the function - * @returns {Immediate} - */ - setImmediate(fn, ...args) { - const immediate = setImmediate(fn, ...args); - this._immediates.add(immediate); - return immediate; - } - - /** - * Clears an immediate. - * @param {Immediate} immediate Immediate to cancel - */ - clearImmediate(immediate) { - clearImmediate(immediate); - this._immediates.delete(immediate); + if (this.rest.sweepInterval) clearInterval(this.rest.sweepInterval); } /** diff --git a/src/client/Client.js b/src/client/Client.js index 7377b7a80..b28d5faf8 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -159,7 +159,10 @@ class Client extends BaseClient { this.readyAt = null; if (this.options.messageSweepInterval > 0) { - this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000); + this.sweepMessageInterval = setInterval( + this.sweepMessages.bind(this), + this.options.messageSweepInterval * 1000, + ).unref(); } } @@ -242,6 +245,8 @@ class Client extends BaseClient { */ destroy() { super.destroy(); + if (this.sweepMessageInterval) clearInterval(this.sweepMessageInterval); + this.ws.destroy(); this.token = null; } diff --git a/src/client/actions/GuildDelete.js b/src/client/actions/GuildDelete.js index 21d8ebadc..b27d90228 100644 --- a/src/client/actions/GuildDelete.js +++ b/src/client/actions/GuildDelete.js @@ -56,7 +56,7 @@ class GuildDeleteAction extends Action { } scheduleForDeletion(id) { - this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout); + setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout).unref(); } } diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index 945ba3403..ff0268a70 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -336,9 +336,9 @@ class WebSocketManager extends EventEmitter { if (this.packetQueue.length) { const item = this.packetQueue.shift(); - this.client.setImmediate(() => { + setImmediate(() => { this.handlePacket(item.packet, item.shard); - }); + }).unref(); } if (packet && PacketHandlers[packet.t]) { diff --git a/src/client/websocket/WebSocketShard.js b/src/client/websocket/WebSocketShard.js index 4d2aa64d0..ab5b92164 100644 --- a/src/client/websocket/WebSocketShard.js +++ b/src/client/websocket/WebSocketShard.js @@ -455,7 +455,7 @@ class WebSocketShard extends EventEmitter { checkReady() { // Step 0. Clear the ready timeout, if it exists if (this.readyTimeout) { - this.manager.client.clearTimeout(this.readyTimeout); + clearTimeout(this.readyTimeout); this.readyTimeout = null; } // Step 1. If we don't have any other guilds pending, we are ready @@ -475,7 +475,7 @@ class WebSocketShard extends EventEmitter { return; } // Step 2. Create a 15s timeout that will mark the shard as ready if there are still unavailable guilds - this.readyTimeout = this.manager.client.setTimeout(() => { + this.readyTimeout = setTimeout(() => { this.debug(`Shard did not receive any more guild packets in 15 seconds. Unavailable guild count: ${this.expectedGuilds.size}`); @@ -484,7 +484,7 @@ class WebSocketShard extends EventEmitter { this.status = Status.READY; this.emit(ShardEvents.ALL_READY, this.expectedGuilds); - }, 15000); + }, 15000).unref(); } /** @@ -496,16 +496,16 @@ class WebSocketShard extends EventEmitter { if (time === -1) { if (this.helloTimeout) { this.debug('Clearing the HELLO timeout.'); - this.manager.client.clearTimeout(this.helloTimeout); + clearTimeout(this.helloTimeout); this.helloTimeout = null; } return; } this.debug('Setting a HELLO timeout for 20s.'); - this.helloTimeout = this.manager.client.setTimeout(() => { + this.helloTimeout = setTimeout(() => { this.debug('Did not receive HELLO in time. Destroying and connecting again.'); this.destroy({ reset: true, closeCode: 4009 }); - }, 20000); + }, 20000).unref(); } /** @@ -517,15 +517,15 @@ class WebSocketShard extends EventEmitter { if (time === -1) { if (this.heartbeatInterval) { this.debug('Clearing the heartbeat interval.'); - this.manager.client.clearInterval(this.heartbeatInterval); + clearInterval(this.heartbeatInterval); this.heartbeatInterval = null; } return; } this.debug(`Setting a heartbeat interval for ${time}ms.`); // Sanity checks - if (this.heartbeatInterval) this.manager.client.clearInterval(this.heartbeatInterval); - this.heartbeatInterval = this.manager.client.setInterval(() => this.sendHeartbeat(), time); + if (this.heartbeatInterval) clearInterval(this.heartbeatInterval); + this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), time).unref(); } /** @@ -668,10 +668,10 @@ class WebSocketShard extends EventEmitter { if (this.ratelimit.remaining === 0) return; if (this.ratelimit.queue.length === 0) return; if (this.ratelimit.remaining === this.ratelimit.total) { - this.ratelimit.timer = this.manager.client.setTimeout(() => { + this.ratelimit.timer = setTimeout(() => { this.ratelimit.remaining = this.ratelimit.total; this.processQueue(); - }, this.ratelimit.time); + }, this.ratelimit.time).unref(); } while (this.ratelimit.remaining > 0) { const item = this.ratelimit.queue.shift(); @@ -741,7 +741,7 @@ class WebSocketShard extends EventEmitter { this.ratelimit.remaining = this.ratelimit.total; this.ratelimit.queue.length = 0; if (this.ratelimit.timer) { - this.manager.client.clearTimeout(this.ratelimit.timer); + clearTimeout(this.ratelimit.timer); this.ratelimit.timer = null; } } diff --git a/src/managers/GuildManager.js b/src/managers/GuildManager.js index 7f380278c..2553ddbf5 100644 --- a/src/managers/GuildManager.js +++ b/src/managers/GuildManager.js @@ -217,7 +217,7 @@ class GuildManager extends CachedManager { const handleGuild = guild => { if (guild.id === data.id) { - this.client.clearTimeout(timeout); + clearTimeout(timeout); this.client.removeListener(Events.GUILD_CREATE, handleGuild); this.client.decrementMaxListeners(); resolve(guild); @@ -226,11 +226,11 @@ class GuildManager extends CachedManager { this.client.incrementMaxListeners(); this.client.on(Events.GUILD_CREATE, handleGuild); - const timeout = this.client.setTimeout(() => { + const timeout = setTimeout(() => { this.client.removeListener(Events.GUILD_CREATE, handleGuild); this.client.decrementMaxListeners(); resolve(this.client.guilds._add(data)); - }, 10000); + }, 10000).unref(); return undefined; }, reject), ); diff --git a/src/managers/GuildMemberManager.js b/src/managers/GuildMemberManager.js index 1c923f468..227b7317b 100644 --- a/src/managers/GuildMemberManager.js +++ b/src/managers/GuildMemberManager.js @@ -372,7 +372,7 @@ class GuildMemberManager extends CachedManager { (limit && fetchedMembers.size >= limit) || i === chunk.count ) { - this.client.clearTimeout(timeout); + clearTimeout(timeout); this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); this.client.decrementMaxListeners(); let fetched = option ? fetchedMembers : this.cache; @@ -380,11 +380,11 @@ class GuildMemberManager extends CachedManager { resolve(fetched); } }; - const timeout = this.client.setTimeout(() => { + const timeout = setTimeout(() => { this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); this.client.decrementMaxListeners(); reject(new Error('GUILD_MEMBERS_TIMEOUT')); - }, time); + }, time).unref(); this.client.incrementMaxListeners(); this.client.on(Events.GUILD_MEMBERS_CHUNK, handler); }); diff --git a/src/rest/APIRequest.js b/src/rest/APIRequest.js index f51361656..c20f1ccf8 100644 --- a/src/rest/APIRequest.js +++ b/src/rest/APIRequest.js @@ -59,14 +59,14 @@ class APIRequest { } const controller = new AbortController(); - const timeout = this.client.setTimeout(() => controller.abort(), this.client.options.restRequestTimeout); + const timeout = setTimeout(() => controller.abort(), this.client.options.restRequestTimeout).unref(); return fetch(url, { method: this.method, headers, agent, body, signal: controller.signal, - }).finally(() => this.client.clearTimeout(timeout)); + }).finally(() => clearTimeout(timeout)); } } diff --git a/src/rest/RESTManager.js b/src/rest/RESTManager.js index 56bf20990..8d919f575 100644 --- a/src/rest/RESTManager.js +++ b/src/rest/RESTManager.js @@ -18,10 +18,9 @@ class RESTManager { this.globalReset = null; this.globalDelay = null; if (client.options.restSweepInterval > 0) { - const interval = client.setInterval(() => { + this.sweepInterval = setInterval(() => { this.handlers.sweep(handler => handler._inactive); - }, client.options.restSweepInterval * 1000); - interval.unref(); + }, client.options.restSweepInterval * 1000).unref(); } } diff --git a/src/rest/RequestHandler.js b/src/rest/RequestHandler.js index 128267669..749dd403c 100644 --- a/src/rest/RequestHandler.js +++ b/src/rest/RequestHandler.js @@ -71,10 +71,10 @@ class RequestHandler { globalDelayFor(ms) { return new Promise(resolve => { - this.manager.client.setTimeout(() => { + setTimeout(() => { this.manager.globalDelay = null; resolve(); - }, ms); + }, ms).unref(); }); } diff --git a/src/structures/GuildTemplate.js b/src/structures/GuildTemplate.js index 6697e535d..09e94c4a4 100644 --- a/src/structures/GuildTemplate.js +++ b/src/structures/GuildTemplate.js @@ -122,7 +122,7 @@ class GuildTemplate extends Base { const handleGuild = guild => { if (guild.id === data.id) { - client.clearTimeout(timeout); + clearTimeout(timeout); resolveGuild(guild); } }; @@ -130,7 +130,7 @@ class GuildTemplate extends Base { client.incrementMaxListeners(); client.on(Events.GUILD_CREATE, handleGuild); - const timeout = client.setTimeout(() => resolveGuild(client.guilds._add(data)), 10000); + const timeout = setTimeout(() => resolveGuild(client.guilds._add(data)), 10000).unref(); }); } diff --git a/src/structures/interfaces/Collector.js b/src/structures/interfaces/Collector.js index 64e8b0029..29229489b 100644 --- a/src/structures/interfaces/Collector.js +++ b/src/structures/interfaces/Collector.js @@ -84,8 +84,8 @@ class Collector extends EventEmitter { this.handleCollect = this.handleCollect.bind(this); this.handleDispose = this.handleDispose.bind(this); - if (options.time) this._timeout = this.client.setTimeout(() => this.stop('time'), options.time); - if (options.idle) this._idletimeout = this.client.setTimeout(() => this.stop('idle'), options.idle); + if (options.time) this._timeout = setTimeout(() => this.stop('time'), options.time).unref(); + if (options.idle) this._idletimeout = setTimeout(() => this.stop('idle'), options.idle).unref(); } /** @@ -108,8 +108,8 @@ class Collector extends EventEmitter { this.emit('collect', ...args); if (this._idletimeout) { - this.client.clearTimeout(this._idletimeout); - this._idletimeout = this.client.setTimeout(() => this.stop('idle'), this.options.idle); + clearTimeout(this._idletimeout); + this._idletimeout = setTimeout(() => this.stop('idle'), this.options.idle).unref(); } } this.checkEnd(); @@ -179,11 +179,11 @@ class Collector extends EventEmitter { if (this.ended) return; if (this._timeout) { - this.client.clearTimeout(this._timeout); + clearTimeout(this._timeout); this._timeout = null; } if (this._idletimeout) { - this.client.clearTimeout(this._idletimeout); + clearTimeout(this._idletimeout); this._idletimeout = null; } this.ended = true; @@ -211,12 +211,12 @@ class Collector extends EventEmitter { */ resetTimer({ time, idle } = {}) { if (this._timeout) { - this.client.clearTimeout(this._timeout); - this._timeout = this.client.setTimeout(() => this.stop('time'), time ?? this.options.time); + clearTimeout(this._timeout); + this._timeout = setTimeout(() => this.stop('time'), time ?? this.options.time).unref(); } if (this._idletimeout) { - this.client.clearTimeout(this._idletimeout); - this._idletimeout = this.client.setTimeout(() => this.stop('idle'), idle ?? this.options.idle); + clearTimeout(this._idletimeout); + this._idletimeout = setTimeout(() => this.stop('idle'), idle ?? this.options.idle).unref(); } } diff --git a/typings/index.d.ts b/typings/index.d.ts index 5e0320f37..b61687fd3 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -165,22 +165,13 @@ export class Base { export class BaseClient extends EventEmitter { public constructor(options?: ClientOptions | WebhookClientOptions); - private _timeouts: Set; - private _intervals: Set; - private _immediates: Set; private readonly api: unknown; private rest: unknown; private decrementMaxListeners(): void; private incrementMaxListeners(): void; public options: ClientOptions | WebhookClientOptions; - public clearInterval(interval: NodeJS.Timeout): void; - public clearTimeout(timeout: NodeJS.Timeout): void; - public clearImmediate(timeout: NodeJS.Immediate): void; public destroy(): void; - public setInterval(fn: (...args: T) => Awaited, delay: number, ...args: T): NodeJS.Timeout; - public setTimeout(fn: (...args: T) => Awaited, delay: number, ...args: T): NodeJS.Timeout; - public setImmediate(fn: (...args: T) => Awaited, ...args: T): NodeJS.Immediate; public toJSON(...props: Record[]): unknown; }