143 lines
4.4 KiB
JavaScript
143 lines
4.4 KiB
JavaScript
/**
|
|
* EventHandler callback
|
|
* @typedef {(event: {[key: string]: any}): void} EventHandlerCallbackType
|
|
*/
|
|
|
|
const sleep = (t) => new Promise((r) => setTimeout(() => r(), t));
|
|
|
|
/**
|
|
* Inherited class for RealtimeAPI and RealtimeClient
|
|
* Adds basic event handling
|
|
* @class
|
|
*/
|
|
export class RealtimeEventHandler {
|
|
/**
|
|
* Create a new RealtimeEventHandler instance
|
|
* @returns {RealtimeEventHandler}
|
|
*/
|
|
constructor() {
|
|
this.eventHandlers = {};
|
|
this.nextEventHandlers = {};
|
|
}
|
|
|
|
/**
|
|
* Clears all event handlers
|
|
* @returns {true}
|
|
*/
|
|
clearEventHandlers() {
|
|
this.eventHandlers = {};
|
|
this.nextEventHandlers = {};
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Listen to specific events
|
|
* @param {string} eventName The name of the event to listen to
|
|
* @param {EventHandlerCallbackType} callback Code to execute on event
|
|
* @returns {EventHandlerCallbackType}
|
|
*/
|
|
on(eventName, callback) {
|
|
this.eventHandlers[eventName] = this.eventHandlers[eventName] || [];
|
|
this.eventHandlers[eventName].push(callback);
|
|
return callback;
|
|
}
|
|
|
|
/**
|
|
* Listen for the next event of a specified type
|
|
* @param {string} eventName The name of the event to listen to
|
|
* @param {EventHandlerCallbackType} callback Code to execute on event
|
|
* @returns {EventHandlerCallbackType}
|
|
*/
|
|
onNext(eventName, callback) {
|
|
this.nextEventHandlers[eventName] = this.nextEventHandlers[eventName] || [];
|
|
this.nextEventHandlers[eventName].push(callback);
|
|
return callback;
|
|
}
|
|
|
|
/**
|
|
* Turns off event listening for specific events
|
|
* Calling without a callback will remove all listeners for the event
|
|
* @param {string} eventName
|
|
* @param {EventHandlerCallbackType} [callback]
|
|
* @returns {true}
|
|
*/
|
|
off(eventName, callback) {
|
|
const handlers = this.eventHandlers[eventName] || [];
|
|
if (callback) {
|
|
const index = handlers.indexOf(callback);
|
|
if (index === -1) {
|
|
throw new Error(
|
|
`Could not turn off specified event listener for "${eventName}": not found as a listener`,
|
|
);
|
|
}
|
|
handlers.splice(index, 1);
|
|
} else {
|
|
delete this.eventHandlers[eventName];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Turns off event listening for the next event of a specific type
|
|
* Calling without a callback will remove all listeners for the next event
|
|
* @param {string} eventName
|
|
* @param {EventHandlerCallbackType} [callback]
|
|
* @returns {true}
|
|
*/
|
|
offNext(eventName, callback) {
|
|
const nextHandlers = this.nextEventHandlers[eventName] || [];
|
|
if (callback) {
|
|
const index = nextHandlers.indexOf(callback);
|
|
if (index === -1) {
|
|
throw new Error(
|
|
`Could not turn off specified next event listener for "${eventName}": not found as a listener`,
|
|
);
|
|
}
|
|
nextHandlers.splice(index, 1);
|
|
} else {
|
|
delete this.nextEventHandlers[eventName];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Waits for next event of a specific type and returns the payload
|
|
* @param {string} eventName
|
|
* @param {number|null} [timeout]
|
|
* @returns {Promise<{[key: string]: any}|null>}
|
|
*/
|
|
async waitForNext(eventName, timeout = null) {
|
|
const t0 = Date.now();
|
|
let nextEvent;
|
|
this.onNext(eventName, (event) => (nextEvent = event));
|
|
while (!nextEvent) {
|
|
if (timeout) {
|
|
const t1 = Date.now();
|
|
if (t1 - t0 > timeout) {
|
|
return null;
|
|
}
|
|
}
|
|
await sleep(1);
|
|
}
|
|
return nextEvent;
|
|
}
|
|
|
|
/**
|
|
* Executes all events in the order they were added, with .on() event handlers executing before .onNext() handlers
|
|
* @param {string} eventName
|
|
* @param {any} event
|
|
* @returns {true}
|
|
*/
|
|
dispatch(eventName, event) {
|
|
const handlers = [].concat(this.eventHandlers[eventName] || []);
|
|
for (const handler of handlers) {
|
|
handler(event);
|
|
}
|
|
const nextHandlers = [].concat(this.nextEventHandlers[eventName] || []);
|
|
for (const nextHandler of nextHandlers) {
|
|
nextHandler(event);
|
|
}
|
|
delete this.nextEventHandlers[eventName];
|
|
return true;
|
|
}
|
|
}
|