-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
140 lines (112 loc) · 3.84 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const EMPTY_FUNCTION = (): void => {
//
};
export default class PromisedWebsocket {
private socket: WebSocket | null = null;
private messageBuffer: any[] = [];
private timeoutTimer: any = null;
private resolve: Function = EMPTY_FUNCTION;
private reject: Function = EMPTY_FUNCTION;
private defaultTimeout = 0;
constructor(defaultTimeout?: number) {
if (defaultTimeout !== undefined) {
this.defaultTimeout = defaultTimeout;
}
}
public async open(url: string, timeout?: number, protocols?: string | string[]): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (this.socket !== null) {
reject('Socket is already open.');
return;
}
this.resolve = resolve;
this.reject = reject;
this.socket = new WebSocket(url, protocols);
this.socket.onopen = (): void => this.onOpen();
this.socket.onmessage = (e: MessageEvent): void => this.onMessage(e);
this.socket.onclose = (): void => this.onClose();
if (timeout === undefined) {
timeout = this.defaultTimeout;
}
if (timeout > 0) {
this.timeoutTimer = setTimeout((): void => this.onTimeout(), timeout);
}
});
}
public async send(data: string | ArrayBuffer | Blob | ArrayBufferView): Promise<void> {
return new Promise<any>((resolve, reject: any): void => {
if (this.socket === null || this.socket.readyState !== 1) {
reject('Not connected.');
return;
}
this.socket.send(data);
resolve();
});
}
public async recv(timeout?: number): Promise<any> {
return new Promise<any>((resolve, reject: any): void => {
if (this.socket === null || this.socket.readyState !== 1) {
reject('Socket is not open.');
return;
}
if (this.isPendingPromise()) {
reject('Another promise is pending.');
return;
}
this.resolve = resolve;
this.reject = reject;
if (timeout === undefined) {
timeout = this.defaultTimeout;
}
if (timeout > 0) {
this.timeoutTimer = setTimeout((): void => this.onTimeout(), timeout);
}
this.processRecv();
});
}
public async close(): Promise<void> {
return new Promise<any>((resolve: any): void => {
if (this.socket !== null) {
this.socket.close();
this.socket = null;
}
resolve();
});
}
public connected(): boolean {
return this.socket !== null;
}
// internals
private isPendingPromise(): boolean {
return this.resolve !== EMPTY_FUNCTION;
}
private processRecv(): void {
if (this.isPendingPromise() && this.messageBuffer.length > 0) {
const message = this.messageBuffer.shift();
clearTimeout(this.timeoutTimer);
const resolve = this.resolve;
this.resolve = EMPTY_FUNCTION;
resolve(message);
}
}
// events
private onOpen(): void {
clearTimeout(this.timeoutTimer);
const resolve = this.resolve;
this.resolve = EMPTY_FUNCTION;
resolve();
}
private onMessage(event: MessageEvent): void {
this.messageBuffer.push(event.data);
this.processRecv();
}
private onTimeout(): void {
this.resolve = EMPTY_FUNCTION;
this.reject('Operation timeout.');
}
private onClose(): void {
this.socket = null;
this.resolve = EMPTY_FUNCTION;
this.reject('Socket was disconnected.');
}
}