Improve consistency of interface

This commit is contained in:
Jonathan Staab
2023-03-27 15:05:34 -05:00
parent 853b42c1c9
commit 9b6a779397
9 changed files with 223 additions and 165 deletions
+7 -15
View File
@@ -1,32 +1,24 @@
export type EventBusHandler = (...args: any[]) => void
export type EventBusListener = {
id: string
handler: EventBusHandler
}
export class EventBus {
static ANY = Math.random().toString().slice(2)
listeners: Record<string, Array<EventBusListener>> = {}
listeners: Record<string, Array<EventBusHandler>> = {}
on(name: string, handler: EventBusHandler) {
const id = Math.random().toString().slice(2)
this.listeners[name] = this.listeners[name] || ([] as Array<EventBusListener>)
this.listeners[name].push({id, handler})
return id
this.listeners[name] = this.listeners[name] || ([] as Array<EventBusHandler>)
this.listeners[name].push(handler)
}
off(name: string, id: string) {
this.listeners[name] = this.listeners[name].filter(l => l.id !== id)
off(name: string, handler: EventBusHandler) {
this.listeners[name] = this.listeners[name].filter(h => h !== handler)
}
clear() {
this.listeners = {}
}
handle(k: string, ...payload: any) {
for (const {handler} of this.listeners[k] || []) {
for (const handler of this.listeners[k] || []) {
handler(...payload)
}
for (const {handler} of this.listeners[EventBus.ANY] || []) {
for (const handler of this.listeners[EventBus.ANY] || []) {
handler(k, ...payload)
}
}
+106
View File
@@ -0,0 +1,106 @@
import WebSocket from "isomorphic-ws"
import {EventBus} from "./EventBus"
import {Deferred, defer} from "./Deferred"
export class Socket {
ws?: WebSocket
url: string
ready?: Deferred<void>
timeout?: NodeJS.Timeout
queue: string[]
bus: EventBus
status: string
static STATUS = {
NEW: "new",
PENDING: "pending",
CLOSED: "closed",
READY: "ready",
}
constructor(url: string) {
this.ws = undefined
this.url = url
this.ready = undefined
this.timeout = undefined
this.queue = []
this.bus = new EventBus()
this.status = Socket.STATUS.NEW
}
async connect() {
if ([Socket.STATUS.NEW, Socket.STATUS.CLOSED].includes(this.status)) {
if (this.ws) {
console.error("Attempted to connect when already connected", this)
}
this.ready = defer()
this.ws = new WebSocket(this.url)
this.status = Socket.STATUS.PENDING
this.ws.addEventListener("open", () => {
console.log(`Opened connection to ${this.url}`)
this.status = Socket.STATUS.READY
this.ready?.resolve()
})
this.ws.addEventListener("message", e => {
this.queue.push(e.data as string)
if (!this.timeout) {
this.timeout = this.handleMessagesAsync()
}
})
this.ws.addEventListener("error", e => {
console.log(`Error on connection to ${this.url}`)
this.disconnect()
this.ready?.reject()
this.status = Socket.STATUS.CLOSED
})
this.ws.addEventListener("close", () => {
console.log(`Closed connection to ${this.url}`)
this.disconnect()
this.ready?.reject()
this.status = Socket.STATUS.CLOSED
})
}
await this.ready?.catch(() => null)
}
disconnect() {
if (this.ws) {
console.log(`Disconnecting from ${this.url}`)
this.ws.close()
this.ws = undefined
}
}
handleMessages() {
for (const json of this.queue.splice(0, 10)) {
let message
try {
message = JSON.parse(json)
} catch (e) {
continue
}
this.bus.handle('message', message)
}
this.timeout = this.queue.length > 0 ? this.handleMessagesAsync() : undefined
}
handleMessagesAsync() {
return setTimeout(() => this.handleMessages(), 10) as NodeJS.Timeout
}
send(message: any) {
if (this.status === Socket.STATUS.READY) {
if (this.ws?.readyState !== 1) {
console.warn("Send attempted before socket was ready", this)
}
this.ws?.send(JSON.stringify(message))
}
}
}