Files
pythagoras/client/ws.ts

77 lines
2.3 KiB
TypeScript

export class WSClient {
private is_connected: boolean;
private is_closed: boolean;
private sock: WebSocket;
private messages_received: Blob[];
// callbacks
private callbacks_connected: (() => void)[];
private callbacks_receive: ((data: Blob | null) => void)[];
public constructor(addr: string) {
this.is_connected = false;
this.is_closed = false;
this.messages_received = [];
this.callbacks_connected = [];
this.callbacks_receive = [];
this.sock = new WebSocket(addr);
this.sock.binaryType = "blob";
this.sock.addEventListener("open", (_) => {
this.is_connected = true;
for (const callback of this.callbacks_connected) { callback(); }
this.callbacks_connected = [];
});
this.sock.addEventListener("message", (e) => {
const callback = this.callbacks_receive.shift();
if (callback === undefined) { this.messages_received.push(e.data); return; }
callback(e.data);
});
this.sock.addEventListener("error", (_) => { this.end(); });
this.sock.addEventListener("close", (_) => { this.end(); });
}
public async wait_for_connection(): Promise<void> {
if (this.is_closed) { throw new Error("connetion was already closed"); }
if (this.is_connected) { return; }
await new Promise<void>((resolver) => {
this.callbacks_connected.push(resolver);
});
}
private end(): void {
this.is_connected = false;
this.is_closed = true;
for (const callback of this.callbacks_connected) { callback(); }
this.callbacks_connected = [];
for (const callback of this.callbacks_receive) { callback(null); }
this.callbacks_receive = [];
}
/**
* @returns `true` if the send call was placed successfully, `false` if the client is not
* active anymore
*/
public send(message: string | ArrayBufferLike | Blob | ArrayBufferView): boolean {
if (!this.is_connected) { return false; }
this.sock.send(message);
return true;
}
/**
* Waits for a new incoming message or connection close.
*
* @returns a `Blob` if there was an incoming message, `null` if the connection closed before
* a message could be received
*/
public receive(): Promise<Blob | null> {
const queued = this.messages_received.shift();
if (queued !== undefined) { return Promise.resolve(queued); }
if (this.is_closed) { return Promise.resolve(null); }
return new Promise<Blob | null>((resolver) => {
this.callbacks_receive.push(resolver);
});
}
}