Collection of utility functions used in web3.js for Ethereum dApp development
—
Advanced promise helpers including timeout handling, polling mechanisms, and deferred promise implementation with state tracking. These utilities provide sophisticated asynchronous programming capabilities for Web3 applications.
/**
* Checks if object is a Promise
* @param object - Object to check
* @returns true if object is a Promise
*/
function isPromise(object: unknown): boolean;/**
* Waits for promise with timeout (overloaded function)
* @param awaitable - Promise or async function to wait for
* @param timeout - Timeout in milliseconds
* @param error - Optional error to throw on timeout
* @returns Promise that resolves with result or undefined on timeout
*/
function waitWithTimeout<T>(
awaitable: Promise<T> | AsyncFunction<T>,
timeout: number,
error?: Error
): Promise<T | undefined>;/**
* Creates timeout promise that rejects
* @param timeout - Timeout in milliseconds
* @param error - Error to reject with
* @returns Tuple of timer and rejection promise
*/
function rejectIfTimeout(timeout: number, error: Error): [Timer, Promise<never>];/**
* Polls function until result is defined, returns interval ID
* @param func - Async function to poll
* @param interval - Polling interval in milliseconds
* @returns Tuple of promise and timer for cleanup
*/
function pollTillDefinedAndReturnIntervalId<T>(
func: AsyncFunction<T>,
interval: number
): [Promise<Exclude<T, undefined>>, Timer];/**
* Polls function until result is defined
* @deprecated Use pollTillDefinedAndReturnIntervalId instead
* @param func - Async function to poll
* @param interval - Polling interval in milliseconds
* @returns Promise that resolves when result is defined
*/
function pollTillDefined<T>(
func: AsyncFunction<T>,
interval: number
): Promise<Exclude<T, undefined>>;/**
* Rejects if condition met at intervals
* @param cond - Async condition function
* @param interval - Check interval in milliseconds
* @returns Tuple of timer and rejection promise
*/
function rejectIfConditionAtInterval<T>(
cond: AsyncFunction<T | undefined>,
interval: number
): [Timer, Promise<never>];Advanced promise implementation with manual control and state tracking.
/**
* Deferred promise with state tracking and timeout support
* Implements Promise interface and additional Web3DeferredPromiseInterface
*/
class Web3DeferredPromise<T> implements Promise<T>, Web3DeferredPromiseInterface<T> {
/**
* Promise string tag for debugging
*/
readonly [Symbol.toStringTag]: 'Promise';
/**
* Current state of the promise
*/
readonly state: 'pending' | 'fulfilled' | 'rejected';
/**
* Creates a new deferred promise
* @param options - Configuration options
* @param options.timeout - Timeout in milliseconds
* @param options.eagerStart - Whether to start timer immediately
* @param options.timeoutMessage - Custom timeout error message
*/
constructor(options?: {
timeout?: number;
eagerStart?: boolean;
timeoutMessage?: string;
});
/**
* Promise then method
* @param onfulfilled - Success callback
* @param onrejected - Error callback
* @returns New promise
*/
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2>;
/**
* Promise catch method
* @param onrejected - Error callback
* @returns New promise
*/
catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
): Promise<T | TResult>;
/**
* Promise finally method
* @param onfinally - Finally callback
* @returns New promise
*/
finally(onfinally?: (() => void) | null): Promise<T>;
/**
* Manually resolve the promise
* @param value - Value or promise to resolve with
*/
resolve(value: T | PromiseLike<T>): void;
/**
* Manually reject the promise
* @param reason - Rejection reason
*/
reject(reason?: unknown): void;
/**
* Start the timeout timer
*/
startTimer(): void;
}import {
isPromise, waitWithTimeout, pollTillDefinedAndReturnIntervalId
} from "web3-utils";
// Check if object is promise
const maybePromise = fetch('/api/data');
if (isPromise(maybePromise)) {
const result = await maybePromise;
}
// Wait with timeout
try {
const result = await waitWithTimeout(
fetch('/api/slow-endpoint'),
5000, // 5 second timeout
new Error('Request timed out')
);
if (result === undefined) {
console.log('Operation timed out');
} else {
console.log('Got result:', result);
}
} catch (error) {
console.error('Error or timeout:', error);
}import { pollTillDefinedAndReturnIntervalId } from "web3-utils";
// Poll for transaction receipt
async function waitForTransactionReceipt(txHash: string) {
const [receiptPromise, intervalId] = pollTillDefinedAndReturnIntervalId(
async () => {
try {
const receipt = await web3.eth.getTransactionReceipt(txHash);
return receipt; // Returns undefined if not mined yet
} catch (error) {
return undefined; // Continue polling on error
}
},
1000 // Poll every second
);
try {
const receipt = await receiptPromise;
console.log('Transaction mined:', receipt);
return receipt;
} finally {
clearInterval(intervalId); // Clean up interval
}
}
// Poll for account balance changes
async function watchBalance(address: string, expectedAmount: string) {
const [balancePromise, intervalId] = pollTillDefinedAndReturnIntervalId(
async () => {
const balance = await web3.eth.getBalance(address);
return balance >= expectedAmount ? balance : undefined;
},
2000 // Poll every 2 seconds
);
return balancePromise.finally(() => clearInterval(intervalId));
}import { Web3DeferredPromise } from "web3-utils";
// Basic deferred promise
const deferred = new Web3DeferredPromise<string>();
// Check state
console.log(deferred.state); // "pending"
// Resolve manually
setTimeout(() => {
deferred.resolve("Hello World");
}, 1000);
const result = await deferred;
console.log(result); // "Hello World"
console.log(deferred.state); // "fulfilled"
// Deferred promise with timeout
const deferredWithTimeout = new Web3DeferredPromise<number>({
timeout: 5000,
eagerStart: true,
timeoutMessage: "Operation timed out after 5 seconds"
});
// Will automatically reject after 5 seconds if not resolved
try {
// Simulate slow operation
setTimeout(() => {
deferredWithTimeout.resolve(42);
}, 3000);
const result = await deferredWithTimeout;
console.log('Result:', result); // 42
} catch (error) {
console.error('Timeout error:', error);
}import {
Web3DeferredPromise, rejectIfTimeout, rejectIfConditionAtInterval
} from "web3-utils";
// Custom timeout with cleanup
async function fetchWithCustomTimeout<T>(
operation: Promise<T>,
timeoutMs: number
): Promise<T> {
const [timeoutTimer, timeoutPromise] = rejectIfTimeout(
timeoutMs,
new Error(`Operation timed out after ${timeoutMs}ms`)
);
try {
return await Promise.race([operation, timeoutPromise]);
} finally {
clearTimeout(timeoutTimer);
}
}
// Conditional timeout based on external condition
async function fetchWithConditionalTimeout<T>(
operation: Promise<T>,
shouldCancel: () => Promise<boolean>
): Promise<T> {
const [conditionTimer, conditionPromise] = rejectIfConditionAtInterval(
shouldCancel,
1000 // Check every second
);
try {
return await Promise.race([operation, conditionPromise]);
} finally {
clearInterval(conditionTimer);
}
}
// Usage
let cancelled = false;
const result = await fetchWithConditionalTimeout(
longRunningOperation(),
async () => cancelled
);
// Cancel after 10 seconds
setTimeout(() => { cancelled = true; }, 10000);// Type definitions for promise utilities
type AsyncFunction<T, K = unknown> = (...args: K[]) => Promise<T>;
type Timer = ReturnType<typeof setInterval>;
type Timeout = ReturnType<typeof setTimeout>;
interface Web3DeferredPromiseInterface<T> {
readonly state: 'pending' | 'fulfilled' | 'rejected';
resolve(value: T | PromiseLike<T>): void;
reject(reason?: unknown): void;
startTimer(): void;
}Install with Tessl CLI
npx tessl i tessl/npm-web3-utils