From 2c0f4abc8879e7aad552faf2f9b6cfd246a4d27d Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:53:26 +0200 Subject: [PATCH] feat: add `waitFor` utility to wait for a duration Adds `waitFor(milliseconds)`, a promisified `setTimeout` for pausing execution in an async function. This helper is duplicated across many MetaMask projects (test-dapp, metamask-sdk, metamask-extension, ...), so it makes sense to consolidate it here. It lives in `time.ts` next to `Duration`/`inMilliseconds`, validates its input with the module's existing `assertIsNonNegativeInteger`, and composes with `inMilliseconds` for other durations, e.g. `waitFor(inMilliseconds(2, Duration.Second))`. closes #160 --- src/index.test.ts | 1 + src/node.test.ts | 1 + src/time.test.ts | 41 ++++++++++++++++++++++++++++++++++++++++- src/time.ts | 15 +++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/index.test.ts b/src/index.test.ts index f4e41bf3d..36b9c69f8 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 8926d9058..7fd351f48 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 c84f60662..0ea1489e0 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 388c3fd64..20c49056f 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)); +}