-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpoll.ts
76 lines (64 loc) · 1.74 KB
/
poll.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
import asyncRetry = require('async-retry');
import { defaultTimeProvider, isExpired, sleep } from '@paradoxical-io/common';
import { Milliseconds, notNullOrUndefined } from '@paradoxical-io/types';
export type Result<T> =
| {
type: 'completed';
data: T;
}
| { type: 'timeout' };
class NoDataError extends Error {}
/**
* Supports exponential polling backoff
* @param block
* @param opts The retry options for exponential backoff along with when the polling expires
* @param time
*/
export async function exponentialPoll<T>(
block: () => Promise<T | undefined>,
opts: Omit<asyncRetry.Options, 'onRetry'> & { expiresAfter: Milliseconds },
time = defaultTimeProvider()
): Promise<Result<T>> {
const now = time.epochMS();
try {
const result = await asyncRetry(async () => {
if (isExpired(now, opts.expiresAfter, 'ms', time)) {
throw new NoDataError();
}
const resolved = await block();
if (notNullOrUndefined(resolved)) {
return resolved;
}
throw new NoDataError();
}, opts);
return { data: result, type: 'completed' };
} catch (e) {
if (e instanceof NoDataError) {
return { type: 'timeout' };
}
throw e;
}
}
/**
* Polls linearly on a schedule
* @param block
* @param expiresIn
* @param waitTime
* @param time
*/
export async function linearPoll<T>(
block: () => Promise<T | undefined>,
expiresIn: Milliseconds,
waitTime: Milliseconds,
time = defaultTimeProvider()
): Promise<Result<T>> {
const now = time.epochMS();
while (!isExpired(now, expiresIn, 'ms', time)) {
const result = await block();
if (result) {
return { type: 'completed', data: result };
}
await sleep(waitTime);
}
return { type: 'timeout' };
}