/** * Checks whether a condition is satisfied at a specified interval, stopping after a maximum amount * of time (if specified). Returns a Promise that resolves with `true` if the condition was * satisfied during polling (if a maximum time is specified, it can resolve with false if the * condition was not satisfied within the maximum time). * * Usage: * await pollUntil(() => something.is.true, 200, 1000); * * @param {Function} condition the "check" callback that will be called to determine whether the * condition has been satisfied (satisfied === callback returns truthy value) * @param {number} atIntervalMs value in milliseconds specifying how often to poll * @param {number} [forMaximumMs] value in milliseconds specifying the maximum time to poll * @returns {Promise} */ export function pollUntil( condition: () => boolean, atIntervalMs: number, forMaximumMs?: number ) { return new Promise((resolve) => { let timeoutId: ReturnType; const intervalId = setInterval(() => { if (condition()) { resolveWithCleanup(true); } }, atIntervalMs); if (typeof forMaximumMs === "number") { timeoutId = setTimeout(() => { // One last check in case the condition was satisfied right before the end: resolveWithCleanup(condition() || false); }, forMaximumMs); } if (condition()) { // Condition already fulfilled before intervals, no need to keep waiting! resolveWithCleanup(true); } function resolveWithCleanup(success: boolean) { clearInterval(intervalId); clearTimeout(timeoutId); resolve(success); } }); } /** * Returns the same settled value or rejection as `promise`, or rejects after `forMaximumMs` * if it does not settle in time. When `forMaximumMs` is omitted or non-positive, returns * `promise` unchanged. Clears the timeout when `promise` settles first. */ export function waitUntilResolved( promise: Promise, forMaximumMs?: number, timeoutMessage = "Timed out waiting for promise to settle." ): Promise { if (forMaximumMs == null || forMaximumMs <= 0) { return promise; } return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error(timeoutMessage)); }, forMaximumMs); promise.then( (value) => { clearTimeout(timeoutId); resolve(value); }, (reason: unknown) => { clearTimeout(timeoutId); reject(reason); } ); }); }