Skip to content

Commit

Permalink
Set the listerners Disposable
Browse files Browse the repository at this point in the history
  • Loading branch information
yvann committed Oct 18, 2024
1 parent 9cbb6de commit 3235ac6
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 105 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
},
"devDependencies": {
"@jest/globals": "30.0.0-alpha.6",
"@swc/core": "1.7.28",
"@swc/core": "1.7.36",
"@swc/jest": "0.2.36",
"@tsconfig/node20": "20.1.4",
"@types/node": "20.16.11",
"@types/node": "20.16.12",
"jest": "30.0.0-alpha.6",
"prettier": "3.3.3",
"publint": "0.2.11",
Expand Down
97 changes: 46 additions & 51 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,12 @@ export type EventListener<
TName extends EventName<TDataByName>,
> = (eventData: EventData<TDataByName, TName>) => Promisable<any>;

const unsubscribeFromSignalAbortEvent = Symbol(
"unsubscribe from signal's abort event",
);
const preOffHook = Symbol('Pre-Off hook');

type WrappedEventListener<
TDataByName extends EventDataByName,
TName extends EventName<TDataByName>,
> = EventListener<TDataByName, TName> & {
[unsubscribeFromSignalAbortEvent]?: () => void;
};
> = EventListener<TDataByName, TName> & { [preOffHook]?: () => void };

export type EventConfig<
TDataByName extends EventDataByName,
Expand Down Expand Up @@ -86,7 +82,7 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
): void {
if (eventName != null) {
if (listener) {
listener[unsubscribeFromSignalAbortEvent]?.();
listener[preOffHook]?.();

const listeners = this.#listenersByName.get(eventName);
if (listeners?.delete(listener) && !listeners.size) {
Expand All @@ -113,7 +109,7 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
listener: EventListener<TDataByName, TName>,
signal?: AbortSignal | number | null,
onAbort?: OnAbort,
): BoundOff;
): BoundOff & Disposable;

/**
* Subscribe to a bunch of events.
Expand All @@ -123,7 +119,7 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
configByName: EventConfigByName<TDataByName>,
signal?: AbortSignal | number | null,
onAbort?: OnAbort,
): BoundOff;
): BoundOff & Disposable;

public on<TName extends EventName<TDataByName>>(
...args:
Expand All @@ -138,7 +134,7 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
signal?: AbortSignal | number | null,
onAbort?: OnAbort,
]
): BoundOff {
): BoundOff & Disposable {
if (typeof args[0] === 'object' && args[0] != null) {
const [configByName, maybeSignal, maybeOnAbort] = args as [
configByName: EventConfigByName<TDataByName>,
Expand Down Expand Up @@ -168,7 +164,9 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
}
}

return () => offs.forEach((off) => off());
const off = () => offs.forEach((off) => off());

return Object.assign(off, { [Symbol.dispose]: off });
}

const [eventName, listener, maybeSignal, maybeOnAbort] = args as [
Expand All @@ -195,8 +193,6 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
this.#listenersByName.set(eventName, (listeners = new Set()));
}

const off: BoundOff = () => this.off(eventName, wrappedListener);

const wrappedListener: WrappedEventListener<TDataByName, TName> =
eventName === 'error' || eventName === errorMonitor
? listener
Expand All @@ -215,6 +211,8 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
}
};

const off: BoundOff = () => this.off(eventName, wrappedListener);

const signal =
typeof maybeSignal === 'number'
? AbortSignal.timeout(maybeSignal)
Expand All @@ -223,21 +221,26 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
if (signal) {
signal.throwIfAborted();

maybeOnAbort &&
signal.addEventListener('abort', maybeOnAbort, { once: true });
signal.addEventListener('abort', off, { once: true });
const onAbort = maybeOnAbort
? (event: Event) => {
try {
maybeOnAbort(event);
} finally {
off();
}
}
: off;

signal.addEventListener('abort', onAbort, { once: true });

Object.assign(wrappedListener, {
[unsubscribeFromSignalAbortEvent]: () => {
maybeOnAbort && signal.removeEventListener('abort', maybeOnAbort);
signal.removeEventListener('abort', off);
},
[preOffHook]: () => signal.removeEventListener('abort', onAbort),
});
}

listeners.add(wrappedListener);

return off;
return Object.assign(off, { [Symbol.dispose]: off });
}

/**
Expand All @@ -249,7 +252,7 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
listener: EventListener<TDataByName, TName>,
signal?: AbortSignal | number | null,
onAbort?: OnAbort,
): BoundOff {
): BoundOff & Disposable {
const off = this.on(
eventName,
(eventData) => {
Expand Down Expand Up @@ -292,15 +295,8 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
}

public async throwOnError(
maybeSignal?: AbortSignal | number | null,
signal?: AbortSignal | number | null,
): Promise<void> {
const signal =
typeof maybeSignal === 'number'
? AbortSignal.timeout(maybeSignal)
: maybeSignal || undefined;

signal?.throwIfAborted();

let error: any;

try {
Expand Down Expand Up @@ -332,28 +328,27 @@ export class AsyncEventEmitter<TDataByName extends EventDataByName = any> {
signal?.throwIfAborted();

return new Promise((resolve, reject) => {
const onAbort = () => {
offs.forEach((off) => off());

reject(
new AbortError(
`The wait of the "${eventNames
.map(String)
.join(', ')}" events has been aborted`,
{ cause: signal?.reason },
const off = this.on(
Object.fromEntries(
eventNames.map((eventName) => [
eventName,
(eventData) => {
off();

resolve(eventData);
},
]),
) as EventConfigByName<any>,
signal,
() =>
reject(
new AbortError(
`The wait of the "${eventNames
.map(String)
.join(', ')}" events has been aborted`,
signal?.reason && { cause: signal.reason },
),
),
);
};

signal?.addEventListener('abort', onAbort, { once: true });

const offs = eventNames.map((eventName) =>
this.on(eventName, (eventData) => {
offs.forEach((off) => off());
signal?.removeEventListener('abort', onAbort);

resolve(eventData);
}),
);
});
}
Expand Down
104 changes: 52 additions & 52 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -809,10 +809,10 @@ __metadata:
resolution: "@prismamedia/async-event-emitter@workspace:."
dependencies:
"@jest/globals": "npm:30.0.0-alpha.6"
"@swc/core": "npm:1.7.28"
"@swc/core": "npm:1.7.36"
"@swc/jest": "npm:0.2.36"
"@tsconfig/node20": "npm:20.1.4"
"@types/node": "npm:20.16.11"
"@types/node": "npm:20.16.12"
jest: "npm:30.0.0-alpha.6"
prettier: "npm:3.3.3"
publint: "npm:0.2.11"
Expand Down Expand Up @@ -853,92 +853,92 @@ __metadata:
languageName: node
linkType: hard

"@swc/core-darwin-arm64@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-darwin-arm64@npm:1.7.28"
"@swc/core-darwin-arm64@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-darwin-arm64@npm:1.7.36"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard

"@swc/core-darwin-x64@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-darwin-x64@npm:1.7.28"
"@swc/core-darwin-x64@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-darwin-x64@npm:1.7.36"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard

"@swc/core-linux-arm-gnueabihf@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.28"
"@swc/core-linux-arm-gnueabihf@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.36"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard

"@swc/core-linux-arm64-gnu@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-linux-arm64-gnu@npm:1.7.28"
"@swc/core-linux-arm64-gnu@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-linux-arm64-gnu@npm:1.7.36"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard

"@swc/core-linux-arm64-musl@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-linux-arm64-musl@npm:1.7.28"
"@swc/core-linux-arm64-musl@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-linux-arm64-musl@npm:1.7.36"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard

"@swc/core-linux-x64-gnu@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-linux-x64-gnu@npm:1.7.28"
"@swc/core-linux-x64-gnu@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-linux-x64-gnu@npm:1.7.36"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard

"@swc/core-linux-x64-musl@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-linux-x64-musl@npm:1.7.28"
"@swc/core-linux-x64-musl@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-linux-x64-musl@npm:1.7.36"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard

"@swc/core-win32-arm64-msvc@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-win32-arm64-msvc@npm:1.7.28"
"@swc/core-win32-arm64-msvc@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-win32-arm64-msvc@npm:1.7.36"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard

"@swc/core-win32-ia32-msvc@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-win32-ia32-msvc@npm:1.7.28"
"@swc/core-win32-ia32-msvc@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-win32-ia32-msvc@npm:1.7.36"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard

"@swc/core-win32-x64-msvc@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core-win32-x64-msvc@npm:1.7.28"
"@swc/core-win32-x64-msvc@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core-win32-x64-msvc@npm:1.7.36"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard

"@swc/core@npm:1.7.28":
version: 1.7.28
resolution: "@swc/core@npm:1.7.28"
"@swc/core@npm:1.7.36":
version: 1.7.36
resolution: "@swc/core@npm:1.7.36"
dependencies:
"@swc/core-darwin-arm64": "npm:1.7.28"
"@swc/core-darwin-x64": "npm:1.7.28"
"@swc/core-linux-arm-gnueabihf": "npm:1.7.28"
"@swc/core-linux-arm64-gnu": "npm:1.7.28"
"@swc/core-linux-arm64-musl": "npm:1.7.28"
"@swc/core-linux-x64-gnu": "npm:1.7.28"
"@swc/core-linux-x64-musl": "npm:1.7.28"
"@swc/core-win32-arm64-msvc": "npm:1.7.28"
"@swc/core-win32-ia32-msvc": "npm:1.7.28"
"@swc/core-win32-x64-msvc": "npm:1.7.28"
"@swc/core-darwin-arm64": "npm:1.7.36"
"@swc/core-darwin-x64": "npm:1.7.36"
"@swc/core-linux-arm-gnueabihf": "npm:1.7.36"
"@swc/core-linux-arm64-gnu": "npm:1.7.36"
"@swc/core-linux-arm64-musl": "npm:1.7.36"
"@swc/core-linux-x64-gnu": "npm:1.7.36"
"@swc/core-linux-x64-musl": "npm:1.7.36"
"@swc/core-win32-arm64-msvc": "npm:1.7.36"
"@swc/core-win32-ia32-msvc": "npm:1.7.36"
"@swc/core-win32-x64-msvc": "npm:1.7.36"
"@swc/counter": "npm:^0.1.3"
"@swc/types": "npm:^0.1.12"
"@swc/types": "npm:^0.1.13"
peerDependencies:
"@swc/helpers": "*"
dependenciesMeta:
Expand All @@ -965,7 +965,7 @@ __metadata:
peerDependenciesMeta:
"@swc/helpers":
optional: true
checksum: 10/a477e79387ecc8b68c2bdbbdc88cc61f27a02c5d00f0d77134f9e2de166786a4ee9f7388d6ffd44fc01bfef5311a15cc3132052bab72fb43246dc42705fedb60
checksum: 10/d78438192b8d956ba5d221915f81f3e31ac14d64188d2cd0f048f7c527c58fe7e04860c54f45c82b09db330e81b584b7bed17724e010495f7c4686555bdb3fa0
languageName: node
linkType: hard

Expand All @@ -989,7 +989,7 @@ __metadata:
languageName: node
linkType: hard

"@swc/types@npm:^0.1.12":
"@swc/types@npm:^0.1.13":
version: 0.1.13
resolution: "@swc/types@npm:0.1.13"
dependencies:
Expand Down Expand Up @@ -1080,12 +1080,12 @@ __metadata:
languageName: node
linkType: hard

"@types/node@npm:20.16.11":
version: 20.16.11
resolution: "@types/node@npm:20.16.11"
"@types/node@npm:20.16.12":
version: 20.16.12
resolution: "@types/node@npm:20.16.12"
dependencies:
undici-types: "npm:~6.19.2"
checksum: 10/6d2f92b7b320c32ba0c2bc54d21651bd21690998a2e27f00d15019d4db3e0ec30fce85332efed5e37d4cda078ff93ea86ee3e92b76b7a25a9b92a52a039b60b2
checksum: 10/689badb5af2a1a03553a6d21880fa4aabb8cf028b7db1a03be889c0026047a780ac37c83df5dca036f02f5dc3cc4000254fa40d2cadd5df0e9bd6f43dae6eac6
languageName: node
linkType: hard

Expand Down

0 comments on commit 3235ac6

Please sign in to comment.