diff --git a/lib/util/poll-action.function.spec.ts b/lib/util/poll-action.function.spec.ts index d4b163b4..0623d5fb 100644 --- a/lib/util/poll-action.function.spec.ts +++ b/lib/util/poll-action.function.spec.ts @@ -118,4 +118,28 @@ describe("poll-action", () => { expect(action).toBeCalledTimes(1); expect((end - start)).toBeLessThan(updateInterval); }); + + it("should fail if action does not resolve within timeout", async () => { + // GIVEN + const updateInterval = 100; + const maxDuration = 200; + const action = jest.fn(() => { + return new Promise((_, reject) => { + setTimeout(() => reject(), 300); + }) + }); + + // WHEN + const start = Date.now(); + try { + await timeout(updateInterval, maxDuration, action); + } catch (e) { + expect(e).toEqual(`Action timed out after ${maxDuration} ms`); + } + const end = Date.now(); + + // THEN + expect(action).toBeCalledTimes(1); + expect((end - start)).toBeGreaterThanOrEqual(maxDuration); + }); }); diff --git a/lib/util/poll-action.function.ts b/lib/util/poll-action.function.ts index 53575e49..acfacb68 100644 --- a/lib/util/poll-action.function.ts +++ b/lib/util/poll-action.function.ts @@ -1,41 +1,45 @@ export function timeout(updateIntervalMs: number, maxDurationMs: number, action: (...params: any) => Promise): Promise { return new Promise((resolve, reject) => { let interval: NodeJS.Timeout; - const maxTimeout = setTimeout( - () => { - clearTimeout(maxTimeout); - if (interval) { - clearTimeout(interval); - } - reject(`Action timed out after ${maxDurationMs} ms`); - }, - maxDurationMs - ); - const startInterval = () => { - interval = setTimeout(function intervalFunc() { - action().then((result) => { - if (!result) { - interval = setTimeout(intervalFunc, updateIntervalMs); - } else { - clearTimeout(maxTimeout); - clearTimeout(interval); - resolve(result); - } - }).catch(() => { - interval = setTimeout(intervalFunc, updateIntervalMs); - }); - }, updateIntervalMs); - }; + let timerCleaned = false + + function executeInterval() { + action().then(validateResult).catch(handleRejection); + } - action().then((result) => { + function validateResult(result: R){ if (!result) { - startInterval(); + interval = setTimeout(executeInterval, updateIntervalMs); } else { - clearTimeout(maxTimeout); + cleanupTimer(); resolve(result); } - }).catch(() => { - startInterval(); - }); + } + + function handleRejection() { + if(!timerCleaned){ + interval = setTimeout(executeInterval, updateIntervalMs); + } + } + + function cleanupTimer(){ + timerCleaned = true + if(maxTimeout){ + clearTimeout(maxTimeout); + } + if(interval){ + clearTimeout(interval); + } + } + + const maxTimeout = setTimeout( + () => { + cleanupTimer(); + reject(`Action timed out after ${maxDurationMs} ms`); + }, + maxDurationMs + ); + + executeInterval() }); }