-
Notifications
You must be signed in to change notification settings - Fork 233
/
asyncUtils.ts
96 lines (79 loc) · 2.44 KB
/
asyncUtils.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
import {
Act,
WaitOptions,
WaitForOptions,
WaitForValueToChangeOptions,
WaitForNextUpdateOptions,
AsyncUtils
} from '../types'
import { createTimeoutController } from '../helpers/createTimeoutController'
import { TimeoutError } from '../helpers/error'
const DEFAULT_INTERVAL = 50
const DEFAULT_TIMEOUT = 1000
function asyncUtils(act: Act, addResolver: (callback: () => void) => void): AsyncUtils {
const wait = async (callback: () => boolean | void, { interval, timeout }: WaitOptions) => {
const checkResult = () => {
const callbackResult = callback()
return callbackResult ?? callbackResult === undefined
}
const timeoutSignal = createTimeoutController(timeout)
const waitForResult = async () => {
while (true) {
const intervalSignal = createTimeoutController(interval)
timeoutSignal.onTimeout(() => intervalSignal.cancel())
await intervalSignal.wrap(new Promise<void>(addResolver))
if (checkResult() || timeoutSignal.timedOut) {
return
}
}
}
if (!checkResult()) {
await act(() => timeoutSignal.wrap(waitForResult()))
}
return !timeoutSignal.timedOut
}
const waitFor = async (
callback: () => boolean | void,
{ interval = DEFAULT_INTERVAL, timeout = DEFAULT_TIMEOUT }: WaitForOptions = {}
) => {
const safeCallback = () => {
try {
return callback()
} catch (error: unknown) {
return false
}
}
const result = await wait(safeCallback, { interval, timeout })
if (!result && timeout) {
throw new TimeoutError(waitFor, timeout)
}
}
const waitForValueToChange = async (
selector: () => unknown,
{ interval = DEFAULT_INTERVAL, timeout = DEFAULT_TIMEOUT }: WaitForValueToChangeOptions = {}
) => {
const initialValue = selector()
const result = await wait(() => selector() !== initialValue, { interval, timeout })
if (!result && timeout) {
throw new TimeoutError(waitForValueToChange, timeout)
}
}
const waitForNextUpdate = async ({
timeout = DEFAULT_TIMEOUT
}: WaitForNextUpdateOptions = {}) => {
let updated = false
addResolver(() => {
updated = true
})
const result = await wait(() => updated, { interval: false, timeout })
if (!result && timeout) {
throw new TimeoutError(waitForNextUpdate, timeout)
}
}
return {
waitFor,
waitForValueToChange,
waitForNextUpdate
}
}
export { asyncUtils }