diff --git a/src/core/asyncUtils.ts b/src/core/asyncUtils.ts index fe44c715..a7424036 100644 --- a/src/core/asyncUtils.ts +++ b/src/core/asyncUtils.ts @@ -7,7 +7,7 @@ import { AsyncUtils } from '../types' -import { resolveAfter, callAfter } from '../helpers/promises' +import { createTimeoutController } from '../helpers/createTimeoutController' import { TimeoutError } from '../helpers/error' const DEFAULT_INTERVAL = 50 @@ -20,37 +20,26 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn return callbackResult ?? callbackResult === undefined } + const timeoutSignal = createTimeoutController(timeout) + const waitForResult = async () => { while (true) { - await Promise.race( - [ - new Promise((resolve) => addResolver(resolve)), - interval && resolveAfter(interval) - ].filter(Boolean) - ) - - if (checkResult()) { + const intervalSignal = createTimeoutController(interval) + timeoutSignal.onTimeout(() => intervalSignal.cancel()) + + await intervalSignal.wrap(new Promise(addResolver)) + + if (checkResult() || timeoutSignal.timedOut) { return } } } - let timedOut = false - if (!checkResult()) { - if (timeout) { - const timeoutPromise = () => - callAfter(() => { - timedOut = true - }, timeout) - - await act(() => Promise.race([waitForResult(), timeoutPromise()])) - } else { - await act(waitForResult) - } + await act(() => timeoutSignal.wrap(waitForResult())) } - return !timedOut + return !timeoutSignal.timedOut } const waitFor = async ( diff --git a/src/helpers/createTimeoutController.ts b/src/helpers/createTimeoutController.ts new file mode 100644 index 00000000..643d3768 --- /dev/null +++ b/src/helpers/createTimeoutController.ts @@ -0,0 +1,39 @@ +import { WaitOptions } from '../types' + +function createTimeoutController(timeout: WaitOptions['timeout']) { + let timeoutId: NodeJS.Timeout + const timeoutCallbacks: Array<() => void> = [] + + const timeoutController = { + onTimeout(callback: () => void) { + timeoutCallbacks.push(callback) + }, + wrap(promise: Promise) { + return new Promise((resolve, reject) => { + timeoutController.timedOut = false + timeoutController.onTimeout(resolve) + + if (timeout) { + timeoutId = setTimeout(() => { + timeoutController.timedOut = true + timeoutCallbacks.forEach((callback) => callback()) + resolve() + }, timeout) + } + + promise + .then(resolve) + .catch(reject) + .finally(() => timeoutController.cancel()) + }) + }, + cancel() { + clearTimeout(timeoutId) + }, + timedOut: false + } + + return timeoutController +} + +export { createTimeoutController } diff --git a/src/helpers/promises.ts b/src/helpers/promises.ts deleted file mode 100644 index 2fa89e5f..00000000 --- a/src/helpers/promises.ts +++ /dev/null @@ -1,10 +0,0 @@ -function resolveAfter(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)) -} - -async function callAfter(callback: () => void, ms: number) { - await resolveAfter(ms) - callback() -} - -export { resolveAfter, callAfter }