UNPKG

@uupaa/messagepassing

Version:

Implementation of lightweight message passing logic for TypeScript.

123 lines (109 loc) 4.08 kB
export type Selector = string|number; export interface MessageSubscriber { onmessage(selector:Selector, options:any):any|void, } export type MessageOptions = number|string|undefined|object|Array<number>|Array<string>; export class MessageResult { private _result:Map<MessageSubscriber, any> = new Map(); set(subscriber:MessageSubscriber, value:any):void { this._result.set(subscriber, value); } get(subscriber:MessageSubscriber):any { return this._result.get(subscriber); } has(subscriber:MessageSubscriber):any { return this._result.has(subscriber); } list():Array<any> { return Array.from(this._result.values()); } } export class MessagePassing { private _subscribers:Set<MessageSubscriber> = new Set(); private _selectors:Map<MessageSubscriber, Array<Selector>> = new Map(); to(...subscribers:Array<MessageSubscriber>):Message { const to = new Set(subscribers.length ? subscribers.concat() // broadcast : this._subscribers.keys()); // unicast, multicast return new Message(this._selectors, to); } register(subscriber:MessageSubscriber, selectors:Array<Selector> = ["ping"]):MessagePassing { if (!subscriber) { throw new TypeError(`Invalid subscriber: ${subscriber}`); } if (!Array.isArray(selectors)) { throw new TypeError(`Invalid selectors: ${selectors}`); } if (typeof subscriber.onmessage !== "function") { throw new TypeError(`${subscriber.constructor.name} has not onmessage function`); } if (selectors.filter(selector => !selector).length) { throw new TypeError(`Invalid selectors: selectors has null, from ${subscriber.constructor.name}`); } this._subscribers.add(subscriber); this._selectors.set(subscriber, selectors.slice()); // shallow copy return this; } unregister(subscriber:MessageSubscriber):MessagePassing { this._subscribers.delete(subscriber); this._selectors.delete(subscriber); return this; } unregisterAll():MessagePassing { this._subscribers.clear(); this._selectors.clear(); return this; } } export class Message { private _selectors:Map<MessageSubscriber, Array<Selector>>; private _to:Set<MessageSubscriber>; // unique array constructor(selectors:Map<MessageSubscriber, Array<Selector>>, to:Set<MessageSubscriber>) { this._selectors = selectors; this._to = to; } remove(subscriber:MessageSubscriber):Message { this._to.delete(subscriber) return this; } send(selector:Selector, options:MessageOptions = undefined):MessageResult { const to = Array.from(this._to.values()); const result = new MessageResult(); try { for (let i = 0, iz = to.length; i < iz; ++i) { const subscriber:MessageSubscriber = to[i]; if (subscriber) { const selectors = this._selectors.get(subscriber); // [ "ping", ... ] if (selectors && selectors.includes(selector) ) { const r:any = subscriber.onmessage(selector, options); result.set(subscriber, r); } else { //console.warn(`Selector (${selector}) not allowed by the subscriber (${subscriber.constructor.name}) have been ignored.`); } } } } catch (err) { console.error(err); } return result; } post(selector:Selector, options:MessageOptions = undefined):void { const to = Array.from(this._to.values()); try { setTimeout(() => { for (let i = 0, iz = to.length; i < iz; ++i) { const subscriber:MessageSubscriber = to[i]; if (subscriber) { const selectors = this._selectors.get(subscriber); // [ "ping", ... ] if (selectors && selectors.includes(selector) ) { subscriber.onmessage(selector, options); } else { //console.warn(`Selector (${selector}) not allowed by the subscriber (${subscriber.constructor.name}) have been ignored.`); } } } }, 0); } catch (err) { console.error(err); } } }