tickle/modules/utils/makeEventEmitter.mjs

55 lines
1.8 KiB
JavaScript
Raw Permalink Normal View History

2023-06-06 22:45:16 +00:00
//@ts-check
/// <reference path="makeEventEmitter.d.ts"/>
/**
* Returns a native browser event target that is properly typed.
* Three major differences with a classical event target:
*
* 1. The emitter's methods are bound and can be passed to other objects
* 2. The emitter has an `abort` property and a `signal` property that can be
* used to abort all listeners (you have to explicitely pass it though, it's
* not automatic)
* 3. `dispatchEvent` has a different signature `(type, event)` rather than just
* `event`. This is because there is no way to enforce a string & details
* tuple on a CustomEvent using Typescript or JSDocs.
* @template {CustomEventMap} EvtMap
* @returns {CustomEventEmitter<EvtMap>}
*/
export const makeEventEmitter = () => {
let abortController = new AbortController();
const eventEmitter = new EventTarget();
const addEventListener = eventEmitter.addEventListener.bind(eventEmitter);
const removeEventListener =
eventEmitter.removeEventListener.bind(eventEmitter);
/**
* Dispatches a custom event to all listeners of that event.
* @type {CustomEventEmitter<EvtMap>["dispatchEvent"]}
*/
const dispatchEvent = (type, detail) => {
const event = new CustomEvent(type, { detail });
eventEmitter.dispatchEvent(event);
};
/**
* Aborts any eventListener, fetch, or other process that received the signal.
* resets the abort controller and signal (they are new instances)
* @param {any} reason
*/
const abort = (reason) => {
abortController.abort(reason);
abortController = new AbortController();
};
return {
dispatchEvent,
addEventListener,
removeEventListener,
abort,
get signal() {
return abortController.signal;
},
};
};
export default makeEventEmitter