UNPKG

mini-signals

Version:
132 lines (131 loc) 3.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MiniSignal = void 0; const MINI_SIGNAL_KEY = Symbol('SIGNAL'); function isMiniSignalNodeRef(obj) { return typeof obj === 'object' && MINI_SIGNAL_KEY in obj; } class MiniSignal { constructor() { /** * A Symbol that is used to guarantee the uniqueness of the MiniSignal * instance. */ this._symbol = Symbol('MiniSignal'); this._refMap = new WeakMap(); this._head = undefined; this._tail = undefined; this._dispatching = false; } hasListeners() { return this._head != null; } /** * Dispatches a signal to all registered listeners. */ dispatch(...args) { if (this._dispatching) { throw new Error('MiniSignal#dispatch(): Signal already dispatching.'); } let node = this._head; if (node == null) return false; this._dispatching = true; while (node != null) { node.fn(...args); node = node.next; } this._dispatching = false; return true; } /** * Register a new listener. */ add(fn) { if (typeof fn !== 'function') { throw new Error('MiniSignal#add(): First arg must be a Function.'); } return this._createRef(this._addNode({ fn })); } /** * Remove binding object. */ detach(sym) { if (!isMiniSignalNodeRef(sym)) { throw new Error('MiniSignal#detach(): First arg must be a MiniSignal listener reference.'); } if (sym[MINI_SIGNAL_KEY] !== this._symbol) { throw new Error('MiniSignal#detach(): MiniSignal listener does not belong to this MiniSignal.'); } const node = this._refMap.get(sym); if (!node) return this; // already detached this._refMap.delete(sym); this._disconnectNode(node); this._destroyNode(node); return this; } /** * Detach all listeners. */ detachAll() { let n = this._head; if (n == null) return this; this._head = this._tail = undefined; this._refMap = new WeakMap(); while (n != null) { this._destroyNode(n); n = n.next; } return this; } _destroyNode(node) { node.fn = undefined; node.prev = undefined; } _disconnectNode(node) { if (node === this._head) { // first node this._head = node.next; if (node.next == null) { this._tail = undefined; } } else if (node === this._tail) { // last node this._tail = node.prev; if (this._tail != null) { this._tail.next = undefined; } } if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } } _addNode(node) { if (this._head == null) { this._head = node; this._tail = node; } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this._tail.next = node; node.prev = this._tail; this._tail = node; } return node; } _createRef(node) { const sym = { [MINI_SIGNAL_KEY]: this._symbol }; this._refMap.set(sym, node); return sym; } _getRef(sym) { return this._refMap.get(sym); } } exports.MiniSignal = MiniSignal;