/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
import { isNullish } from '@theqrl/web3-validator';
export type Timer = ReturnType;
export type Timeout = ReturnType;
/**
* An alternative to the node function `isPromise` that exists in `util/types` because it is not available on the browser.
* @param object - to check if it is a `Promise`
* @returns `true` if it is an `object` or a `function` that has a `then` function. And returns `false` otherwise.
*/
export function isPromise(object: unknown): boolean {
return (
(typeof object === 'object' || typeof object === 'function') &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
typeof (object as { then: unknown }).then === 'function'
);
}
export type AsyncFunction = (...args: K[]) => Promise;
export function waitWithTimeout(
awaitable: Promise | AsyncFunction,
timeout: number,
error: Error,
): Promise;
export function waitWithTimeout(
awaitable: Promise | AsyncFunction,
timeout: number,
): Promise;
/**
* Wait for a promise but interrupt it if it did not resolve within a given timeout.
* If the timeout reached, before the promise code resolve, either throw an error if an error object was provided, or return `undefined`.
* @param awaitable - The promise or function to wait for.
* @param timeout - The timeout in milliseconds.
* @param error - (Optional) The error to throw if the timeout reached.
*/
export async function waitWithTimeout(
awaitable: Promise | AsyncFunction,
timeout: number,
error?: Error,
): Promise {
let timeoutId: Timeout | undefined;
const result = await Promise.race([
awaitable instanceof Promise ? awaitable : awaitable(),
new Promise((resolve, reject) => {
timeoutId = setTimeout(() => (error ? reject(error) : resolve(undefined)), timeout);
}),
]);
if (timeoutId) {
clearTimeout(timeoutId);
}
if (result instanceof Error) {
throw result;
}
return result;
}
/**
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached. It returns promise and intervalId.
* @param func - The function to call.
* @param interval - The interval in milliseconds.
*/
export function pollTillDefinedAndReturnIntervalId(
func: AsyncFunction,
interval: number,
): [Promise>, Timer] {
let intervalId: Timer | undefined;
const polledRes = new Promise>((resolve, reject) => {
intervalId = setInterval(
(function intervalCallbackFunc() {
(async () => {
try {
const res = await waitWithTimeout(func, interval);
if (!isNullish(res)) {
clearInterval(intervalId);
resolve(res as unknown as Exclude);
}
} catch (error) {
clearInterval(intervalId);
reject(error);
}
})() as unknown;
return intervalCallbackFunc;
})(), // this will immediate invoke first call
interval,
);
});
return [polledRes as unknown as Promise>, intervalId!];
}
/**
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached.
* pollTillDefinedAndReturnIntervalId() function should be used instead of pollTillDefined if you need IntervalId in result.
* This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId().
* @param func - The function to call.
* @param interval - The interval in milliseconds.
*/
export async function pollTillDefined(
func: AsyncFunction,
interval: number,
): Promise> {
return pollTillDefinedAndReturnIntervalId(func, interval)[0];
}
/**
* Enforce a timeout on a promise, so that it can be rejected if it takes too long to complete
* @param timeout - The timeout to enforced in milliseconds.
* @param error - The error to throw if the timeout is reached.
* @returns A tuple of the timeout id and the promise that will be rejected if the timeout is reached.
*
* @example
* ```ts
* const [timerId, promise] = web3.utils.rejectIfTimeout(100, new Error('time out'));
* ```
*/
export function rejectIfTimeout(timeout: number, error: Error): [Timer, Promise] {
let timeoutId: Timer | undefined;
const rejectOnTimeout = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(error);
}, timeout);
});
return [timeoutId!, rejectOnTimeout];
}
/**
* Sets an interval that repeatedly executes the given cond function with the specified interval between each call.
* If the condition is met, the interval is cleared and a Promise that rejects with the returned value is returned.
* @param cond - The function/confition to call.
* @param interval - The interval in milliseconds.
* @returns - an array with the interval ID and the Promise.
*/
export function rejectIfConditionAtInterval(
cond: AsyncFunction,
interval: number,
): [Timer, Promise] {
let intervalId: Timer | undefined;
const rejectIfCondition = new Promise((_, reject) => {
intervalId = setInterval(() => {
(async () => {
const error = await cond();
if (error) {
clearInterval(intervalId);
reject(error);
}
})() as unknown;
}, interval);
});
return [intervalId!, rejectIfCondition];
}