Skip to content

Commit 0d5a07a

Browse files
committed
(#49) Polling function
1 parent 770dd46 commit 0d5a07a

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import {timeout} from "./poll-action.function";
2+
3+
describe("poll-action", () => {
4+
it("should timeout after maxDuration if action rejects", async () => {
5+
// GIVEN
6+
const updateInterval = 200;
7+
const maxDuration = 1000;
8+
const action = jest.fn(() => {
9+
console.log(`Polling...`);
10+
return Promise.reject(false);
11+
});
12+
13+
// WHEN
14+
const start = Date.now();
15+
try {
16+
await timeout(updateInterval, maxDuration, action);
17+
} catch (e) {
18+
expect(e).toBe(`Action timed out after ${maxDuration} ms`);
19+
}
20+
const end = Date.now();
21+
22+
// THEN
23+
expect((end - start)).toBeGreaterThanOrEqual(maxDuration);
24+
expect(action).toBeCalledTimes((maxDuration / updateInterval));
25+
});
26+
27+
it("should timeout after maxDuration if action resolve != true", async () => {
28+
// GIVEN
29+
const updateInterval = 200;
30+
const maxDuration = 1000;
31+
const action = jest.fn(async () => {
32+
console.log(`Polling...`);
33+
return false;
34+
});
35+
36+
// WHEN
37+
const start = Date.now();
38+
try {
39+
await timeout(updateInterval, maxDuration, action);
40+
} catch (e) {
41+
expect(e).toEqual(`Action timed out after ${maxDuration} ms`);
42+
}
43+
const end = Date.now();
44+
45+
// THEN
46+
expect((end - start)).toBeGreaterThanOrEqual(maxDuration);
47+
expect(action).toBeCalledTimes((maxDuration / updateInterval));
48+
});
49+
50+
it("should resolve after updateInterval if action resolves", async () => {
51+
// GIVEN
52+
const updateInterval = 200;
53+
const maxDuration = 1000;
54+
const action = jest.fn(() => {
55+
console.log(`Polling...`);
56+
return Promise.resolve(true);
57+
});
58+
59+
// WHEN
60+
const start = Date.now();
61+
await timeout(updateInterval, maxDuration, action);
62+
const end = Date.now();
63+
64+
// THEN
65+
expect((end - start)).toBeLessThan(updateInterval);
66+
expect(action).toBeCalledTimes(1);
67+
});
68+
69+
it("should resolve after updateInterval if action resolves != true", async () => {
70+
// GIVEN
71+
const updateInterval = 200;
72+
const maxDuration = 1000;
73+
const action = jest.fn(async () => {
74+
console.log(`Polling...`);
75+
return true;
76+
});
77+
78+
// WHEN
79+
const start = Date.now();
80+
await timeout(updateInterval, maxDuration, action);
81+
const end = Date.now();
82+
83+
// THEN
84+
expect((end - start)).toBeLessThan(updateInterval);
85+
expect(action).toBeCalledTimes(1);
86+
});
87+
88+
it("should retry until action succeeds", async () => {
89+
// GIVEN
90+
const updateInterval = 200;
91+
const maxDuration = 1000;
92+
const delay = 2.2 * updateInterval;
93+
const action = jest.fn(() => {
94+
console.log(`Polling...`);
95+
const interval = (Date.now() - start);
96+
return new Promise<boolean>((resolve, reject) => (interval > delay) ? resolve(true) : reject());
97+
});
98+
99+
// WHEN
100+
const start = Date.now();
101+
await timeout(updateInterval, maxDuration, action);
102+
const end = Date.now();
103+
104+
// THEN
105+
expect((end - start)).toBeGreaterThanOrEqual(delay);
106+
expect(action).toBeCalledTimes(4);
107+
});
108+
109+
it("should fail after timeout if timeout < retry interval", async () => {
110+
// GIVEN
111+
const updateInterval = 1000;
112+
const maxDuration = 200;
113+
const action = jest.fn(() => Promise.resolve(false));
114+
115+
// WHEN
116+
const start = Date.now();
117+
try {
118+
await timeout(updateInterval, maxDuration, action);
119+
} catch (e) {
120+
expect(e).toEqual(`Action timed out after ${maxDuration} ms`);
121+
}
122+
const end = Date.now();
123+
124+
// THEN
125+
expect(action).toBeCalledTimes(1);
126+
expect((end - start)).toBeLessThan(updateInterval);
127+
});
128+
});

lib/util/poll-action.function.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export function timeout<R>(updateIntervalMs: number, maxDurationMs: number, action: (...params: any) => Promise<R>): Promise<R> {
2+
return new Promise<R>((resolve, reject) => {
3+
let interval: NodeJS.Timeout;
4+
const timeout = setTimeout(
5+
() => {
6+
clearTimeout(timeout);
7+
if (interval) {
8+
clearTimeout(interval);
9+
}
10+
reject(`Action timed out after ${maxDurationMs} ms`);
11+
},
12+
maxDurationMs
13+
);
14+
const startInterval = () => {
15+
interval = setTimeout(function intervalFunc() {
16+
action().then((result) => {
17+
if (!result) {
18+
interval = setTimeout(intervalFunc, updateIntervalMs);
19+
} else {
20+
clearTimeout(timeout);
21+
clearTimeout(interval);
22+
resolve(result);
23+
}
24+
}).catch(() => {
25+
interval = setTimeout(intervalFunc, updateIntervalMs);
26+
});
27+
}, updateIntervalMs);
28+
};
29+
30+
action().then((result) => {
31+
if (!result) {
32+
startInterval();
33+
} else {
34+
clearTimeout(timeout);
35+
resolve(result);
36+
}
37+
}).catch(() => {
38+
startInterval();
39+
});
40+
});
41+
}

0 commit comments

Comments
 (0)