diff --git a/test/abstract_client.ts b/test/abstract_client.ts index 8a489b749..df468b69c 100644 --- a/test/abstract_client.ts +++ b/test/abstract_client.ts @@ -6,7 +6,7 @@ import sinon from 'sinon' import fs from 'fs' import levelStore from 'mqtt-level-store' import Store from '../src/lib/store' -import serverBuilder from './server_helpers_for_client_tests' +import serverBuilderFn from './server_helpers_for_client_tests' import handlePubrel from '../src/lib/handlers/pubrel' import TeardownHelper from './helpers/TeardownHelper' import handle from '../src/lib/handlers/index' @@ -50,16 +50,23 @@ const fakeTimersOptions = { export default function abstractTest(server, config, ports) { const version = config.protocolVersion || 4 + const teardownHelper = new TeardownHelper() function connect(opts?: IClientOptions | string) { if (typeof opts === 'string') { opts = { host: opts } } opts = { ...config, ...opts } as IClientOptions - return mqtt.connect(opts) + const instance = mqtt.connect(opts) + teardownHelper.addClient(instance) + return instance } - const teardownHelper = new TeardownHelper() + function serverBuilder(...args: Parameters) { + const instance = serverBuilderFn(...args) + teardownHelper.addServer(instance) + return instance + } async function beforeEachExec() { await teardownHelper.runAll() @@ -74,6 +81,9 @@ export default function abstractTest(server, config, ports) { after(afterExec) describe('closing', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should emit close if stream closes', function _test(t, done) { const client = connect() @@ -254,14 +264,19 @@ export default function abstractTest(server, config, ports) { it('should emit end even on a failed connection', function _test(t, done) { const client = connect({ host: 'this_hostname_should_not_exist' }) + let timeoutEmitted = false const timeout = setTimeout(() => { + timeoutEmitted = true done(new Error('Disconnected client has failed to emit end')) }, 500) client.once('end', () => { - clearTimeout(timeout) - done() + // Prevent hanging test if `end` is not emitted before timeout + if (!timeoutEmitted) { + clearTimeout(timeout) + done() + } }) // after 200ms manually invoke client.end @@ -298,6 +313,9 @@ export default function abstractTest(server, config, ports) { }) describe('connecting', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should connect to the broker', function _test(t, done) { const client = connect() client.on('error', done) @@ -326,7 +344,7 @@ export default function abstractTest(server, config, ports) { server.once('client', (serverClient) => { serverClient.once('connect', (packet) => { assert.strictEqual(packet.clean, true) - done() + client.end((err) => done(err)) }) }) }) @@ -361,17 +379,24 @@ export default function abstractTest(server, config, ports) { }) it('should require a clientId with clean=false', function _test(t, done) { + let errorCaught = false + try { const client = connect({ clean: false }) client.on('error', (err) => { done(err) }) } catch (err) { + errorCaught = true assert.strictEqual( err.message, 'Missing clientId for unclean clients', ) done() + } finally { + if (!errorCaught) { + done(new Error('Client should have thrown an error')) + } } }) @@ -384,7 +409,7 @@ export default function abstractTest(server, config, ports) { server.once('client', (serverClient) => { serverClient.once('connect', (packet) => { assert.include(packet.clientId, 'testclient') - done() + client.end((err) => done(err)) }) }) }) @@ -473,6 +498,9 @@ export default function abstractTest(server, config, ports) { }) describe('handling offline states', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should emit offline event once when the client transitions from connected states to disconnected ones', function _test(t, done) { const client = connect({ reconnectPeriod: 20 }) @@ -498,6 +526,9 @@ export default function abstractTest(server, config, ports) { }) describe('topic validations when subscribing', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should be ok for well-formated topics', function _test(t, done) { const client = connect() client.subscribe( @@ -549,7 +580,7 @@ export default function abstractTest(server, config, ports) { } assert.isArray(granted2) assert.isEmpty(granted2) - done() + client.end((err3) => done(err3)) }) }) }) @@ -629,9 +660,7 @@ export default function abstractTest(server, config, ports) { client.once('connect', () => { assert.strictEqual(client.queue.length, 0) - setTimeout(() => { - client.end(true, done) - }, 10) + client.end((err) => done(err)) }) }) @@ -641,9 +670,7 @@ export default function abstractTest(server, config, ports) { client.publish('test', 'test', { qos: 0 }) assert.strictEqual(client.queue.length, 0) client.on('connect', () => { - setTimeout(() => { - client.end(true, done) - }, 10) + client.end((err) => done(err)) }) }) @@ -656,9 +683,7 @@ export default function abstractTest(server, config, ports) { client.unsubscribe('test') assert.strictEqual(client.queue.length, 2) client.on('connect', () => { - setTimeout(() => { - client.end(true, done) - }, 10) + client.end((err) => done(err)) }) }) @@ -706,8 +731,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -719,7 +742,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, queueQoSZero: true, }) - teardownHelper.addClient(client) client.on('packetreceive', (packet) => { if (packet.cmd === 'connack') { setImmediate(() => { @@ -783,8 +805,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - const clientOptions = { port: ports.PORTAND72, host: 'localhost', @@ -799,8 +819,6 @@ export default function abstractTest(server, config, ports) { server2.listen(ports.PORTAND72, () => { client = connect(clientOptions) - teardownHelper.addClient(client) - client.once('close', () => { client.once('connect', () => { client.publish('test', 'payload2', { qos: 1 }, () => { @@ -833,9 +851,7 @@ export default function abstractTest(server, config, ports) { client.on('connect', () => { assert.isTrue(called) - setTimeout(() => { - client.end(true, done) - }, 10) + client.end((err) => done(err)) }) }) @@ -1158,8 +1174,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND72, () => { client = connect({ port: ports.PORTAND72, @@ -1169,8 +1183,6 @@ export default function abstractTest(server, config, ports) { reconnectPeriod: 0, }) - teardownHelper.addClient(client) - client.once('connect', () => { client.publish( 'a', @@ -1238,8 +1250,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND103, () => { client = connect({ port: ports.PORTAND103, @@ -1249,8 +1259,6 @@ export default function abstractTest(server, config, ports) { reconnectPeriod: 0, }) - teardownHelper.addClient(client) - client.once('connect', () => { client.publish( 'a', @@ -1340,6 +1348,17 @@ export default function abstractTest(server, config, ports) { }) function testQosHandleMessage(qos, done) { + teardownHelper.add({ executeOnce: true, order: 1 }, () => { + if (clock) { + clock.restore() + } + }) + + const clock = sinon.useFakeTimers({ + ...fakeTimersOptions, + toFake: ['setTimeout'], + }) + const client = connect() let messageEventCount = 0 @@ -1353,10 +1372,14 @@ export default function abstractTest(server, config, ports) { if (handleMessageCount === 10) { setTimeout(() => { client.end(true, done) - }) + }, 10) + + clock.tick(10) } callback() }, 10) + + clock.tick(10) } client.on('message', (topic, message, packet) => { @@ -1773,8 +1796,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -1786,8 +1807,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client.on('connect', () => { if (!reconnect) { client.publish('topic', 'payload1', { qos: 1 }) @@ -1877,6 +1896,9 @@ export default function abstractTest(server, config, ports) { }) describe('unsubscribing', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should send an unsubscribe packet (offline)', function _test(t, done) { const client = connect() let received = false @@ -2010,7 +2032,8 @@ export default function abstractTest(server, config, ports) { let clock: sinon.SinonFakeTimers // eslint-disable-next-line - beforeEach(() => { + beforeEach(async () => { + await beforeEachExec() clock = sinon.useFakeTimers(fakeTimersOptions) }) @@ -2018,6 +2041,8 @@ export default function abstractTest(server, config, ports) { clock.restore() }) + after(afterExec) + it('should send ping at keepalive interval', function _test(t, done) { const interval = 3000 const client = connect({ keepalive: interval / 1000 }) @@ -2101,64 +2126,79 @@ export default function abstractTest(server, config, ports) { }) const reschedulePing = (reschedulePings: boolean) => { - it(`should ${ - !reschedulePings ? 'not ' : '' - }reschedule pings if publishing at a higher rate than keepalive and reschedulePings===${reschedulePings}`, function _test(t, done) { - const intervalMs = 3000 - const client = connect({ - keepalive: intervalMs / 1000, - reschedulePings, - }) - - const spyReschedule = sinon.spy( - client, - '_reschedulePing' as any, - ) + it( + `should ${ + !reschedulePings ? 'not ' : '' + }reschedule pings if publishing at a higher rate than keepalive and reschedulePings===${reschedulePings}`, + { + timeout: 4000, + }, + function _test(t, done) { + clock.restore() - let received = 0 + teardownHelper.add( + { + executeOnce: true, + order: 1, + }, + () => { + if (localClock) { + localClock.restore() + } + }, + ) - client.on('packetreceive', (packet) => { - if (packet.cmd === 'puback') { - process.nextTick(() => { - clock.tick(intervalMs) + const localClock = sinon.useFakeTimers({ + ...fakeTimersOptions, + toFake: ['setTimeout'], + }) + const intervalMs = 3000 + const client = connect({ + keepalive: intervalMs / 1000, + reschedulePings, + }) - received++ + const spyReschedule = sinon.spy( + client, + '_reschedulePing' as any, + ) - if (received === 2) { - if (reschedulePings) { - assert.strictEqual( - spyReschedule.callCount, - received, - ) - } else { - assert.strictEqual( - spyReschedule.callCount, - 0, - ) + let received = 0 + + client.on('packetreceive', (packet) => { + if (packet.cmd === 'puback') { + process.nextTick(() => { + localClock.tick(intervalMs) + + ++received + + if (received === 2) { + if (reschedulePings) { + assert.strictEqual( + spyReschedule.callCount, + received, + ) + } else { + assert.strictEqual( + spyReschedule.callCount, + 0, + ) + } + client.end((err) => done(err)) } - client.end(true, done) - } - }) - - clock.tick(1) - } - }) - - server.once('client', (serverClient) => { - serverClient.on('publish', () => { - // needed to trigger the setImmediate inside server publish listener and send suback - clock.tick(1) + }) + } }) - }) - client.once('connect', () => { - // reset call count (it's called also on connack) - spyReschedule.resetHistory() - // use qos1 so the puback is received (to reschedule ping) - client.publish('foo', 'bar', { qos: 1 }) - client.publish('foo', 'bar', { qos: 1 }) - }) - }) + client.once('connect', () => { + // reset call count (it's called also on connack) + spyReschedule.resetHistory() + // use qos1 so the puback is received (to reschedule ping) + client.publish('foo', 'bar', { qos: 1 }) + client.publish('foo', 'bar', { qos: 1 }) + }) + }, + ) } reschedulePing(true) @@ -2168,7 +2208,7 @@ export default function abstractTest(server, config, ports) { it(`should shift ping on pingresp when reschedulePings===${reschedulePings}`, function _test(t, done) { const intervalMs = 3000 - let client = connect({ + const client = connect({ keepalive: intervalMs / 1000, reschedulePings, }) @@ -2180,7 +2220,6 @@ export default function abstractTest(server, config, ports) { process.nextTick(() => { assert.strictEqual(spy.callCount, 1) client.end(true, done) - client = null }) } }) @@ -2201,6 +2240,9 @@ export default function abstractTest(server, config, ports) { }) describe('pinging', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should setup keepalive manager', function _test(t, done) { const client = connect({ keepalive: 3 }) client.once('connect', () => { @@ -2227,10 +2269,6 @@ export default function abstractTest(server, config, ports) { t.after(() => { clock.restore() - if (client) { - client.end(true) - throw new Error('Test timed out') - } }) const options: IClientOptions = { @@ -2238,7 +2276,7 @@ export default function abstractTest(server, config, ports) { reconnectPeriod: 5000, } - let client = connect(options) + const client = connect(options) client.once('connect', () => { client.once('error', (err) => { @@ -2246,7 +2284,6 @@ export default function abstractTest(server, config, ports) { client.once('connect', () => { client.end(true, done) clock.tick(100) - client = null }) }) @@ -2271,12 +2308,9 @@ export default function abstractTest(server, config, ports) { t.after(() => { clock.restore() - if (client) { - client.end(true) - } }) - let client = connect({ keepalive: 10 }) + const client = connect({ keepalive: 10 }) client.once('close', () => { done(new Error('Client closed connection')) }) @@ -2295,7 +2329,6 @@ export default function abstractTest(server, config, ports) { client.removeAllListeners('close') client.end(true, done) clock.tick(100) - client = null } }) @@ -2306,6 +2339,9 @@ export default function abstractTest(server, config, ports) { }) describe('subscribing', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should send a subscribe message (offline)', function _test(t, done) { const client = connect() @@ -2313,7 +2349,7 @@ export default function abstractTest(server, config, ports) { server.once('client', (serverClient) => { serverClient.once('subscribe', () => { - done() + client.end((err) => done(err)) }) }) }) @@ -2338,7 +2374,7 @@ export default function abstractTest(server, config, ports) { result.rh = 0 } assert.include(packet.subscriptions[0], result) - done() + client.end((err) => done(err)) }) }) }) @@ -2353,7 +2389,7 @@ export default function abstractTest(server, config, ports) { client.on('packetsend', (packet) => { if (packet.cmd === 'subscribe') { - done() + client.end((err) => done(err)) } }) }) @@ -2368,7 +2404,7 @@ export default function abstractTest(server, config, ports) { client.on('packetreceive', (packet) => { if (packet.cmd === 'suback') { - done() + client.end((err) => done(err)) } }) }) @@ -2464,7 +2500,7 @@ export default function abstractTest(server, config, ports) { } assert.deepStrictEqual(packet.subscriptions, expected) - done() + client.end((err) => done(err)) }) }) }) @@ -2581,6 +2617,9 @@ export default function abstractTest(server, config, ports) { }) describe('receiving messages', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should fire the message event', function _test(t, done) { const client = connect() const testPacket = { @@ -2760,6 +2799,9 @@ export default function abstractTest(server, config, ports) { }) describe('qos handling', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should follow qos 0 semantics (trivial)', function _test(t, done) { const client = connect() const testTopic = 'test' @@ -3159,8 +3201,7 @@ export default function abstractTest(server, config, ports) { const client = connect() client.on('connect', () => { - client.end() - done() // it will raise an exception if called two times + client.end((err) => done(err)) }) }) @@ -3238,15 +3279,33 @@ export default function abstractTest(server, config, ports) { }) it('should always cleanup successfully on reconnection', function _test(t, done) { + teardownHelper.add({ executeOnce: true, order: 1 }, () => { + if (clock) { + clock.restore() + } + }) + + const clock = sinon.useFakeTimers({ + ...fakeTimersOptions, + toFake: ['setTimeout'], + }) + const client = connect({ host: 'this_hostname_should_not_exist', connectTimeout: 0, reconnectPeriod: 1, }) + // bind client.end so that when it is called it is automatically passed in the done callback setTimeout(() => { - client.end(done) - }, 100) + setTimeout(() => { + client.end(done) + }, 10) + + clock.tick(10) + }, 10) + + clock.tick(10) }) it('should emit connack timeout error', function _test(t, done) { @@ -3282,13 +3341,7 @@ export default function abstractTest(server, config, ports) { timeout: 4000, }, function _test(t, done) { - t.after(() => { - // close client if not closed - if (client) { - client.end(true) - } - }) - let client = connect({ reconnectPeriod: 200 }) + const client = connect({ reconnectPeriod: 200 }) let serverPublished = false let clientCalledBack = false @@ -3325,7 +3378,6 @@ export default function abstractTest(server, config, ports) { setImmediate(() => { assert.isTrue(clientCalledBack) client.end(true, done) - client = null }) } }) @@ -3364,14 +3416,7 @@ export default function abstractTest(server, config, ports) { timeout: 4000, }, function _test(t, done) { - t.after(() => { - // close client if not closed - if (client) { - client.end(true) - } - }) - - let client = connect({ reconnectPeriod: 200 }) + const client = connect({ reconnectPeriod: 200 }) let serverPublished = false let clientCalledBack = false @@ -3401,7 +3446,6 @@ export default function abstractTest(server, config, ports) { setImmediate(() => { assert.isTrue(clientCalledBack) client.end(true, done) - client = null }) } }) @@ -3409,14 +3453,7 @@ export default function abstractTest(server, config, ports) { ) it('should not resend in-flight QoS 1 removed publish messages from the client', function _test(t, done) { - t.after(() => { - // close client if not closed - if (client) { - client.end(true) - } - }) - - let client = connect({ reconnectPeriod: 100 }) + const client = connect({ reconnectPeriod: 100 }) let clientCalledBack = false server.once('client', (serverClient) => { @@ -3450,7 +3487,6 @@ export default function abstractTest(server, config, ports) { assert.isTrue(clientCalledBack) client.end(true, (err) => { done(err) - client = null }) }) @@ -3600,8 +3636,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND49, () => { client = connect({ port: ports.PORTAND49, @@ -3609,8 +3643,6 @@ export default function abstractTest(server, config, ports) { reconnectPeriod: 100, }) - teardownHelper.addClient(client) - client.on('reconnect', () => { reconnectEvent = true }) @@ -3679,8 +3711,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3692,8 +3722,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client.on('connect', () => { if (!reconnect) { client.subscribe('test', { qos: 2 }, () => {}) @@ -3728,8 +3756,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3740,8 +3766,6 @@ export default function abstractTest(server, config, ports) { reconnectPeriod: 0, }) - teardownHelper.addClient(client) - client.on('connect', () => { client.subscribe('test', { qos: 2 }, (e) => { if (!e) { @@ -3791,8 +3815,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3804,8 +3826,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client.on('connect', () => { if (!reconnect) { client.publish('topic', 'payload', { qos: 1 }) @@ -3841,8 +3861,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3854,8 +3872,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client.on('connect', () => { if (!reconnect) { client.publish('topic', 'payload', { qos: 2 }) @@ -3896,8 +3912,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3909,8 +3923,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client.on('connect', () => { if (!reconnect) { client.publish( @@ -3983,8 +3995,6 @@ export default function abstractTest(server, config, ports) { }) }) - teardownHelper.addServer(server2) - server2.listen(ports.PORTAND50, () => { client = connect({ port: ports.PORTAND50, @@ -3996,8 +4006,6 @@ export default function abstractTest(server, config, ports) { outgoingStore, }) - teardownHelper.addClient(client) - client['nextId'] = 65535 client.on('connect', () => { @@ -4042,6 +4050,17 @@ export default function abstractTest(server, config, ports) { }) it('should be able to pub/sub if reconnect() is called at out of close handler', function _test(t, done) { + teardownHelper.add({ executeOnce: true, order: 1 }, () => { + if (clock) { + clock.restore() + } + }) + + const clock = sinon.useFakeTimers({ + ...fakeTimersOptions, + toFake: ['setTimeout'], + }) + const client = connect({ reconnectPeriod: 0 }) let tryReconnect = true let reconnectEvent = false @@ -4052,6 +4071,8 @@ export default function abstractTest(server, config, ports) { setTimeout(() => { client.reconnect() }, 100) + + clock.tick(100) } else { assert.isTrue(reconnectEvent) done() @@ -4078,7 +4099,8 @@ export default function abstractTest(server, config, ports) { const connack = version === 5 ? { reasonCode: 0 } : { returnCode: 0 } - beforeEach(() => { + beforeEach(async () => { + await beforeEachExec() cachedClientListeners = server.listeners('client') server.removeAllListeners('client') }) @@ -4090,6 +4112,8 @@ export default function abstractTest(server, config, ports) { }) }) + after(afterExec) + it('should resubscribe even if disconnect is before suback', function _test(t, done) { const client = connect({ reconnectPeriod: 100, ...config }) let subscribeCount = 0 @@ -4152,6 +4176,9 @@ export default function abstractTest(server, config, ports) { }) describe('message id to subscription topic mapping', () => { + beforeEach(beforeEachExec) + after(afterExec) + it('should not create a mapping if resubscribe is disabled', function _test(t, done) { const client = connect({ resubscribe: false }) client.subscribe('test1') diff --git a/test/helpers/TeardownHelper.ts b/test/helpers/TeardownHelper.ts index 774582009..fd91e927f 100644 --- a/test/helpers/TeardownHelper.ts +++ b/test/helpers/TeardownHelper.ts @@ -1,5 +1,6 @@ import type { MqttClient } from 'src' import { randomUUID } from 'node:crypto' +import { isAsyncFunction } from 'node:util/types' import serverBuilder from '../server_helpers_for_client_tests' type ServerBuilderInstance = ReturnType @@ -12,6 +13,14 @@ type AddOptions = { * @default true */ executeOnce?: boolean + /** + * @description + * The order in which the method will be executed. + * If `order===0` the method will be executed after all methods before it that were added. + * + * @default 0 + */ + order?: number } type ResetOptions = { @@ -24,6 +33,11 @@ type ResetOptions = { removeOnce?: boolean } +type Method = + | Promise + | ((...args: any[]) => Promise) + | ((...args: any[]) => any) + /** * @description * Class to help clean the environment or close opened connections after tests finish. @@ -99,7 +113,7 @@ class TeardownHelper { string, { options: AddOptions - method: Promise | ((...args: any[]) => Promise) + method: Method args: any[] } > @@ -132,17 +146,17 @@ class TeardownHelper { * @description * Add a method to be executed */ - add Promise>( + add( options: AddOptions | undefined, - method: Promise | T, - ...args: Parameters + method: Method, + ...args: T ): string { const id = randomUUID() this.#methods.set(id, { method, args, - options: { executeOnce: true, ...options }, + options: { executeOnce: true, order: 0, ...options }, }) return id @@ -235,23 +249,43 @@ class TeardownHelper { return } + const methodStored: (AddOptions & { key: string })[] = [] + + for (const [key, { options }] of this.#methods) { + methodStored.push({ ...options, key }) + } + + methodStored.sort((a, b) => b.order - a.order) const methods: Array> = [] - for (const [id, { method, options, args }] of this.#methods) { + for (const { key, ...options } of methodStored) { + const { method, args } = this.#methods.get(key) + if (method instanceof Promise) { methods.push(method) - } else { + } else if (isAsyncFunction(method)) { const promise = new Promise((resolve, reject) => { method(...args) .then(resolve) .catch(reject) }) + methods.push(promise) + } else { + const promise = new Promise((resolve, reject) => { + try { + const result = method(...args) + resolve(result) + } catch (error) { + reject(error) + } + }) + methods.push(promise) } if (options.executeOnce) { - this.#methods.delete(id) + this.#methods.delete(key) } }