From cd4f7b3b9bf2aacfe5f928f3737da67f3b0d33dd Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 28 Sep 2023 15:22:00 -0300 Subject: [PATCH] Remove ability to pass array of event names to EventEmitter.prototype.once This functionality is implemented by wrapping the listener argument in another listener. This means that the event emitter does not hold a reference to the listener argument (other than that held indirectly through the wrapper) and so it is not possible to remove this listener using `off(..., listener)`. The client library specification does not specify a version of `once` which accepts an array of event names, and we do not advertise it as part of the public API. So, I think the simplest thing is to remove this functionality. Resolves #1452. --- src/common/lib/client/realtimechannel.ts | 6 ++-- src/common/lib/util/eventemitter.ts | 19 ++---------- test/realtime/event_emitter.test.js | 38 ------------------------ 3 files changed, 6 insertions(+), 57 deletions(-) diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index ce6f50d687..6c8a5d06b3 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -165,9 +165,11 @@ class RealtimeChannel extends Channel { // Ignore 'attaching' -- could be just due to to a resume & reattach, should not // call back setOptions until we're definitely attached with the new options (or // else in a terminal state) - this._allChannelChanges.once( + const self = this; + this._allChannelChanges.on( ['attached', 'update', 'detached', 'failed'], - function (this: { event: string }, stateChange: ConnectionStateChange) { + function listener(this: { event: string }, stateChange: ConnectionStateChange) { + self._allChannelChanges.off(listener); switch (this.event) { case 'update': case 'attached': diff --git a/src/common/lib/util/eventemitter.ts b/src/common/lib/util/eventemitter.ts index d24e448e8b..4219d478e5 100644 --- a/src/common/lib/util/eventemitter.ts +++ b/src/common/lib/util/eventemitter.ts @@ -237,14 +237,14 @@ class EventEmitter { * @param event the name of the event to listen to * @param listener the listener to be called */ - once(event?: string | string[] | null, listener?: Function): void; + once(event?: string | null, listener?: Function): void; once(...args: unknown[]): void | Promise { const argCount = args.length; if ((argCount === 0 || (argCount === 1 && typeof args[0] !== 'function')) && Platform.Config.Promise) { const event = args[0]; return new Platform.Config.Promise((resolve) => { - this.once(event as string | string[] | null, resolve); + this.once(event as string | null, resolve); }); } @@ -256,21 +256,6 @@ class EventEmitter { throw new Error('EventEmitter.once(): Invalid arguments:' + Platform.Config.inspect(args)); } this.anyOnce.push(secondArg); - } else if (Utils.isArray(firstArg)) { - const self = this; - const listenerWrapper = function (this: any) { - const innerArgs = Array.prototype.slice.call(arguments); - Utils.arrForEach(firstArg, function (eventName) { - self.off(eventName, listenerWrapper); - }); - if (typeof secondArg !== 'function') { - throw new Error('EventEmitter.once(): Invalid arguments:' + Platform.Config.inspect(args)); - } - secondArg.apply(this, innerArgs); - }; - Utils.arrForEach(firstArg, function (eventName) { - self.on(eventName, listenerWrapper); - }); } else { if (typeof firstArg !== 'string') { throw new Error('EventEmitter.once(): Invalid arguments:' + Platform.Config.inspect(args)); diff --git a/test/realtime/event_emitter.test.js b/test/realtime/event_emitter.test.js index eeb2d91851..85e077694d 100644 --- a/test/realtime/event_emitter.test.js +++ b/test/realtime/event_emitter.test.js @@ -342,31 +342,6 @@ define(['shared_helper', 'chai'], function (helper, chai) { closeAndFinish(done, realtime); }); - it('arrayOfEventsWithOnce', function (done) { - var realtime = helper.AblyRealtime({ autoConnect: false }), - callbackCalled = 0, - eventEmitter = realtime.connection; - - var callback = function (arg) { - callbackCalled += 1; - expect(arg).to.equal('expected'); - }; - - try { - callbackCalled = 0; - eventEmitter.once(['a', 'b', 'c'], callback); - eventEmitter.emit('a', 'expected'); - eventEmitter.emit('b', 'wrong'); - eventEmitter.emit('c', 'wrong'); - expect(callbackCalled).to.equal(1, 'listener called back only once, for the first event emitted'); - } catch (err) { - closeAndFinish(done, realtime, err); - return; - } - - closeAndFinish(done, realtime); - }); - /* check that listeners added in a listener cb are not called during that * emit instance */ it('listenerAddedInListenerCb', function (done) { @@ -481,19 +456,6 @@ define(['shared_helper', 'chai'], function (helper, chai) { closeAndFinish(done, realtime, err); }); }); - - it('arrayOfEventsWithOnce', function (done) { - var realtime = helper.AblyRealtime({ autoConnect: false }), - eventEmitter = realtime.connection; - - const p = eventEmitter.once(['a', 'b', 'c']); - eventEmitter.emit('b'); - p.then(function () { - closeAndFinish(done, realtime); - }).catch(function (err) { - closeAndFinish(done, realtime, err); - }); - }); }); } });