Skip to content

Commit 6c9de33

Browse files
committed
(#241) Finally renamed timout files and made timeout cancellable
1 parent 14b70f3 commit 6c9de33

File tree

3 files changed

+82
-46
lines changed

3 files changed

+82
-46
lines changed

lib/util/poll-action.function.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

lib/util/poll-action.function.spec.ts renamed to lib/util/timeout.function.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {timeout} from "./poll-action.function";
1+
import {timeout} from "./timeout.function";
2+
import AbortController from "node-abort-controller";
23

34
describe("poll-action", () => {
45
it("should timeout after maxDuration if action rejects", async () => {
@@ -160,4 +161,26 @@ describe("poll-action", () => {
160161
done();
161162
}, 500);
162163
});
164+
165+
it("should be externally abortable", async () => {
166+
// GIVEN
167+
const controller = new AbortController();
168+
const signal = controller.signal;
169+
const updateInterval = 100;
170+
const maxDuration = 3000;
171+
const action = jest.fn(() => {
172+
return new Promise<boolean>((_, reject) => {
173+
setTimeout(() => {
174+
reject((undefined as unknown) as boolean);
175+
}, 20);
176+
});
177+
});
178+
179+
// WHEN
180+
const SUT = timeout(updateInterval, maxDuration, action, {signal});
181+
setTimeout(() => controller.abort(), 1000);
182+
183+
// THEN
184+
await expect(SUT).rejects.toBe(`Action aborted by signal`);
185+
});
163186
});

lib/util/timeout.function.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {AbortSignal} from "node-abort-controller";
2+
3+
export interface TimoutConfig {
4+
signal?: AbortSignal
5+
}
6+
7+
export function timeout<R>(updateIntervalMs: number, maxDurationMs: number, action: (...params: any) => Promise<R>, config?: TimoutConfig): Promise<R> {
8+
return new Promise<R>((resolve, reject) => {
9+
let interval: NodeJS.Timeout;
10+
let timerCleaned = false
11+
12+
if (config?.signal) {
13+
config.signal.onabort = () => {
14+
cleanupTimer();
15+
reject(`Action aborted by signal`);
16+
}
17+
}
18+
19+
function executeInterval() {
20+
action().then(validateResult).catch(handleRejection);
21+
}
22+
23+
function validateResult(result: R) {
24+
if (!result && !timerCleaned) {
25+
interval = setTimeout(executeInterval, updateIntervalMs);
26+
} else {
27+
cleanupTimer();
28+
resolve(result);
29+
}
30+
}
31+
32+
function handleRejection() {
33+
if (!timerCleaned) {
34+
interval = setTimeout(executeInterval, updateIntervalMs);
35+
}
36+
}
37+
38+
function cleanupTimer() {
39+
timerCleaned = true
40+
if (maxTimeout) {
41+
clearTimeout(maxTimeout);
42+
}
43+
if (interval) {
44+
clearTimeout(interval);
45+
}
46+
}
47+
48+
const maxTimeout = setTimeout(
49+
() => {
50+
cleanupTimer();
51+
reject(`Action timed out after ${maxDurationMs} ms`);
52+
},
53+
maxDurationMs
54+
);
55+
56+
executeInterval()
57+
});
58+
}

0 commit comments

Comments
 (0)