tickle/modules/utils/makeSignal.mjs

69 lines
1.5 KiB
JavaScript
Raw Permalink Normal View History

2023-06-06 22:45:16 +00:00
//@ts-check
/**
* @template T
* @typedef {(args:T) => void} Listener<T>
*/
/**
* @typedef {{signal?: AbortSignal, once?: boolean}} ListenerOptions
*/
2023-06-07 16:51:18 +00:00
/**
* @template T
* @typedef {{
* connect(listener: Listener<T>, options?: ListenerOptions): () => boolean,
* disconnect(listener: Listener<T>): boolean,
* emit(args: T): void
* disable(): void
* }} Signal<T>
*/
2023-06-06 22:45:16 +00:00
/**
* Returns an event emitter for a specific signal
* The initial passed value is optional, discarded, and only used to provide
* automatic typing where applicable.
* @template T
* @param {T} [_initial]
2023-06-07 16:51:18 +00:00
* @returns {Signal<T>}
2023-06-06 22:45:16 +00:00
*/
export const makeSignal = (_initial) => {
/** @type {Set<Listener<T>>} */
const listeners = new Set();
let enabled = true
/**
*
* @param {Listener<T>} fn
* @param {ListenerOptions} [options]
*/
const connect = (fn, { once, signal } = {}) => {
if (once) {
const _bound = fn;
fn = (args) => {
listeners.delete(fn);
_bound(args);
};
}
listeners.add(fn);
const _disconnect = () => disconnect(fn);
signal && signal.addEventListener("abort", _disconnect);
return _disconnect;
};
/**
* @param {Listener<T>} fn
* @returns
*/
const disconnect = (fn) => listeners.delete(fn);
/**
* @param {T} [args]
* @returns
*/
// @ts-ignore
const emit = (args) => enabled && listeners.forEach((fn) => fn(args));
const disable = () => {enabled = false}
return { connect, disconnect, emit, disable };
};
export default makeSignal