diff --git a/src/index.test.ts b/src/index.test.ts index f4e41bf3..36b9c69f 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -166,6 +166,7 @@ describe('index', () => { "uint8ArrayToMnemonic", "unitMap", "valueToBytes", + "waitFor", "wrapError", ] `); diff --git a/src/node.test.ts b/src/node.test.ts index 8926d905..7fd351f4 100644 --- a/src/node.test.ts +++ b/src/node.test.ts @@ -173,6 +173,7 @@ describe('node', () => { "uint8ArrayToMnemonic", "unitMap", "valueToBytes", + "waitFor", "wrapError", "writeFile", "writeJsonFile", diff --git a/src/time.test.ts b/src/time.test.ts index c84f6066..0ea1489e 100644 --- a/src/time.test.ts +++ b/src/time.test.ts @@ -1,4 +1,4 @@ -import { Duration, inMilliseconds, timeSince } from '.'; +import { Duration, inMilliseconds, timeSince, waitFor } from '.'; describe('time utilities', () => { describe('Duration', () => { @@ -63,3 +63,42 @@ describe('time utilities', () => { }); }); }); + +describe('waitFor', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('resolves once the given duration has elapsed', async () => { + const onResolved = jest.fn(); + const promise = waitFor(Duration.Second).then(onResolved); + + await Promise.resolve(); + jest.advanceTimersByTime(Duration.Second - 1); + await Promise.resolve(); + expect(onResolved).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1); + await promise; + expect(onResolved).toHaveBeenCalledTimes(1); + }); + + it('resolves with undefined for a zero duration', async () => { + const promise = waitFor(0); + jest.advanceTimersByTime(0); + expect(await promise).toBeUndefined(); + }); + + it('rejects for a negative or non-integer duration', async () => { + await expect(waitFor(-1)).rejects.toThrow( + '"milliseconds" must be a non-negative integer. Received: "-1".', + ); + await expect(waitFor(1.5)).rejects.toThrow( + '"milliseconds" must be a non-negative integer. Received: "1.5".', + ); + }); +}); diff --git a/src/time.ts b/src/time.ts index 388c3fd6..20c49056 100644 --- a/src/time.ts +++ b/src/time.ts @@ -71,3 +71,18 @@ export function timeSince(timestamp: number): number { assertIsNonNegativeInteger(timestamp, 'timestamp'); return Date.now() - timestamp; } + +/** + * Waits for the given number of milliseconds. + * + * This is a promisified `setTimeout`, useful for pausing execution in an async + * function. Combine it with {@link inMilliseconds} to wait for a different + * {@link Duration}, for example `waitFor(inMilliseconds(2, Duration.Second))`. + * + * @param milliseconds - The number of milliseconds to wait. + * @returns A promise that resolves once the given duration has elapsed. + */ +export async function waitFor(milliseconds: number): Promise { + assertIsNonNegativeInteger(milliseconds, 'milliseconds'); + return new Promise((resolve) => setTimeout(resolve, milliseconds)); +}