diff options
author | 2021-10-27 21:35:57 +0200 | |
---|---|---|
committer | 2021-10-31 19:26:13 +0100 | |
commit | 2c94b73460452d55a4d0a5542509d1de1d9c071b (patch) | |
tree | fc25067e91109378ce4f09c66e7a9d5f6dba010c | |
parent | chore(web): simplify websocket state machine (diff) | |
download | refinery-2c94b73460452d55a4d0a5542509d1de1d9c071b.tar.gz refinery-2c94b73460452d55a4d0a5542509d1de1d9c071b.tar.zst refinery-2c94b73460452d55a4d0a5542509d1de1d9c071b.zip |
chore(web): refactor PendingTask
-rw-r--r-- | language-web/src/main/js/editor/PendingRequest.ts | 55 | ||||
-rw-r--r-- | language-web/src/main/js/editor/XtextWebSocketClient.ts | 18 | ||||
-rw-r--r-- | language-web/src/main/js/utils/PendingTask.ts | 60 |
3 files changed, 70 insertions, 63 deletions
diff --git a/language-web/src/main/js/editor/PendingRequest.ts b/language-web/src/main/js/editor/PendingRequest.ts deleted file mode 100644 index 49d4c36c..00000000 --- a/language-web/src/main/js/editor/PendingRequest.ts +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | import { getLogger } from '../logging'; | ||
2 | |||
3 | const REQUEST_TIMEOUT_MS = 1000; | ||
4 | |||
5 | const log = getLogger('PendingRequest'); | ||
6 | |||
7 | export class PendingRequest { | ||
8 | private readonly resolveCallback: (value: unknown) => void; | ||
9 | |||
10 | private readonly rejectCallback: (reason?: unknown) => void; | ||
11 | |||
12 | private readonly timeoutCallback: () => void; | ||
13 | |||
14 | private resolved = false; | ||
15 | |||
16 | private timeoutId: NodeJS.Timeout; | ||
17 | |||
18 | constructor( | ||
19 | resolve: (value: unknown) => void, | ||
20 | reject: (reason?: unknown) => void, | ||
21 | timeout: () => void, | ||
22 | ) { | ||
23 | this.resolveCallback = resolve; | ||
24 | this.rejectCallback = reject; | ||
25 | this.timeoutCallback = timeout; | ||
26 | this.timeoutId = setTimeout(() => { | ||
27 | if (!this.resolved) { | ||
28 | this.reject(new Error('Request timed out')); | ||
29 | this.timeoutCallback(); | ||
30 | } | ||
31 | }, REQUEST_TIMEOUT_MS); | ||
32 | } | ||
33 | |||
34 | resolve(value: unknown): void { | ||
35 | if (this.resolved) { | ||
36 | log.warn('Trying to resolve already resolved promise'); | ||
37 | return; | ||
38 | } | ||
39 | this.markResolved(); | ||
40 | this.resolveCallback(value); | ||
41 | } | ||
42 | |||
43 | reject(reason?: unknown): void { | ||
44 | if (this.resolved) { | ||
45 | log.warn('Trying to reject already resolved promise'); | ||
46 | } | ||
47 | this.markResolved(); | ||
48 | this.rejectCallback(reason); | ||
49 | } | ||
50 | |||
51 | private markResolved() { | ||
52 | this.resolved = true; | ||
53 | clearTimeout(this.timeoutId); | ||
54 | } | ||
55 | } | ||
diff --git a/language-web/src/main/js/editor/XtextWebSocketClient.ts b/language-web/src/main/js/editor/XtextWebSocketClient.ts index 489ac03d..5b775500 100644 --- a/language-web/src/main/js/editor/XtextWebSocketClient.ts +++ b/language-web/src/main/js/editor/XtextWebSocketClient.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { nanoid } from 'nanoid'; | 1 | import { nanoid } from 'nanoid'; |
2 | 2 | ||
3 | import { getLogger } from '../logging'; | 3 | import { getLogger } from '../logging'; |
4 | import { PendingRequest } from './PendingRequest'; | 4 | import { PendingTask } from '../utils/PendingTask'; |
5 | import { Timer } from '../utils/Timer'; | 5 | import { Timer } from '../utils/Timer'; |
6 | import { | 6 | import { |
7 | isErrorResponse, | 7 | isErrorResponse, |
@@ -23,6 +23,8 @@ const BACKGROUND_IDLE_TIMEOUT_MS = 5 * 60 * 1000; | |||
23 | 23 | ||
24 | const PING_TIMEOUT_MS = 10 * 1000; | 24 | const PING_TIMEOUT_MS = 10 * 1000; |
25 | 25 | ||
26 | const REQUEST_TIMEOUT_MS = 1000; | ||
27 | |||
26 | const log = getLogger('XtextWebSocketClient'); | 28 | const log = getLogger('XtextWebSocketClient'); |
27 | 29 | ||
28 | type ReconnectHandler = () => Promise<void>; | 30 | type ReconnectHandler = () => Promise<void>; |
@@ -49,7 +51,7 @@ export class XtextWebSocketClient { | |||
49 | 51 | ||
50 | connection!: WebSocket; | 52 | connection!: WebSocket; |
51 | 53 | ||
52 | pendingRequests = new Map<string, PendingRequest>(); | 54 | pendingRequests = new Map<string, PendingTask<unknown>>(); |
53 | 55 | ||
54 | onReconnect: ReconnectHandler; | 56 | onReconnect: ReconnectHandler; |
55 | 57 | ||
@@ -223,14 +225,14 @@ export class XtextWebSocketClient { | |||
223 | id: messageId, | 225 | id: messageId, |
224 | request, | 226 | request, |
225 | } as IXtextWebRequest); | 227 | } as IXtextWebRequest); |
226 | const promise = new Promise((resolve, reject) => { | 228 | log.trace('Sending message', message); |
227 | this.pendingRequests.set(messageId, new PendingRequest(resolve, reject, () => { | 229 | return new Promise((resolve, reject) => { |
230 | const task = new PendingTask(resolve, reject, REQUEST_TIMEOUT_MS, () => { | ||
228 | this.removePendingRequest(messageId); | 231 | this.removePendingRequest(messageId); |
229 | })); | 232 | }); |
233 | this.pendingRequests.set(messageId, task); | ||
234 | this.connection.send(message); | ||
230 | }); | 235 | }); |
231 | log.trace('Sending message', message); | ||
232 | this.connection.send(message); | ||
233 | return promise; | ||
234 | } | 236 | } |
235 | 237 | ||
236 | private handleMessage(messageStr: unknown) { | 238 | private handleMessage(messageStr: unknown) { |
diff --git a/language-web/src/main/js/utils/PendingTask.ts b/language-web/src/main/js/utils/PendingTask.ts new file mode 100644 index 00000000..aa2b11b0 --- /dev/null +++ b/language-web/src/main/js/utils/PendingTask.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { getLogger } from '../logging'; | ||
2 | |||
3 | const log = getLogger('PendingTask'); | ||
4 | |||
5 | export class PendingTask<T> { | ||
6 | private readonly resolveCallback: (value: T) => void; | ||
7 | |||
8 | private readonly rejectCallback: (reason?: unknown) => void; | ||
9 | |||
10 | private resolved = false; | ||
11 | |||
12 | private timeout: NodeJS.Timeout | null; | ||
13 | |||
14 | constructor( | ||
15 | resolveCallback: (value: T) => void, | ||
16 | rejectCallback: (reason?: unknown) => void, | ||
17 | timeoutMs?: number, | ||
18 | timeoutCallback?: () => void, | ||
19 | ) { | ||
20 | this.resolveCallback = resolveCallback; | ||
21 | this.rejectCallback = rejectCallback; | ||
22 | if (timeoutMs) { | ||
23 | this.timeout = setTimeout(() => { | ||
24 | if (!this.resolved) { | ||
25 | this.reject(new Error('Request timed out')); | ||
26 | if (timeoutCallback) { | ||
27 | timeoutCallback(); | ||
28 | } | ||
29 | } | ||
30 | }, timeoutMs); | ||
31 | } else { | ||
32 | this.timeout = null; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | resolve(value: T): void { | ||
37 | if (this.resolved) { | ||
38 | log.warn('Trying to resolve already resolved promise'); | ||
39 | return; | ||
40 | } | ||
41 | this.markResolved(); | ||
42 | this.resolveCallback(value); | ||
43 | } | ||
44 | |||
45 | reject(reason?: unknown): void { | ||
46 | if (this.resolved) { | ||
47 | log.warn('Trying to reject already resolved promise'); | ||
48 | return; | ||
49 | } | ||
50 | this.markResolved(); | ||
51 | this.rejectCallback(reason); | ||
52 | } | ||
53 | |||
54 | private markResolved() { | ||
55 | this.resolved = true; | ||
56 | if (this.timeout !== null) { | ||
57 | clearTimeout(this.timeout); | ||
58 | } | ||
59 | } | ||
60 | } | ||