0xweb
Version:
Contract package manager and other web3 tools
155 lines (139 loc) • 5.26 kB
text/typescript
import { TError } from '@dequanto/models/TError';
import { class_EventEmitter } from 'atma-utils';
import { TFnWithCallback } from './types';
export namespace $promise {
export function wait (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
export function fromEvent<
T extends Pick<class_EventEmitter, 'on' | 'off'>,
TArgs extends Parameters<T['on']>,
> (
eventEmitter: T,
event: TArgs[0],
): Promise<any> {
return new Promise((resolve, reject) => {
const cb = (value) => {
resolve(value);
eventEmitter.off(event, cb)
};
eventEmitter.on(event, cb);
});
}
export function fromCallback <TResult, TArgs extends any[]> (fn: TFnWithCallback<TArgs, TResult>, ...args: TArgs): Promise<TResult> {
return new Promise((resolve, reject) => {
fn(...args, (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result);
});
})
}
export function fromCallbackCtx <TResult, TArgs extends any[]> (ctx: any, fn: TFnWithCallback<TArgs, TResult>, ...args: TArgs): Promise<TResult> {
return new Promise((resolve, reject) => {
fn.call(ctx, ...args, (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result);
});
})
}
export async function caught<T, TErr extends Error = TError > (fn: () => Promise<T>): Promise<{ result?: T, error?: TError}>
export async function caught<T, TErr extends Error = TError & { code?: string | number } > (promise: Promise<T>): Promise<{ result?: T, error?: TError}>
export async function caught<T, TErr extends Error = TError & { code?: string | number } > (mix: Promise<T> | (() => Promise<T>)): Promise<{ result?: T, error?: TError}> {
try {
let promise = typeof mix === 'function' ? mix() : mix;
let result = await promise;
return { result };
} catch (error) {
return { error };
}
}
export function timeout<T extends Promise<any>> (promise: T, ms: number, message?: string): T {
let err = new Error(`Promise timeout in ${ms}ms. ${message}`);
return new Promise((resolve, reject) => {
let completed = false;
let timeout = setTimeout(() => {
if (completed) {
return;
}
completed = true;
reject(err);
}, ms);
promise.then(
result => {
completed = true;
clearTimeout(timeout);
resolve(result);
},
err => {
completed = true;
clearTimeout(timeout);
reject(err);
}
);
}) as T;
}
export function waitForTrue (check: () => boolean | Promise<boolean>, opts?: Parameters<typeof waitForObject>[1]): Promise<any> {
return waitForObject(async () => {
let result = await check();
return [ null, result === true ? {} : null ]
}, opts);
}
export function waitForObject<T>(check: () => Promise<[Error, T?]>, opts?: {
intervalMs?: number
timeoutMs?: number
timeoutMessage?: string | (() => string)
}): Promise<T> {
let start = Date.now();
let completed = false;
let intervalMs = opts?.intervalMs ?? 500;
let timeoutMs = opts?.timeoutMs ?? null;
let timeoutMessage = opts?.timeoutMessage ?? `Waiting for object timeouted`;
return new Promise(async (resolve, reject) => {
async function tick () {
let [ error, result ] = (await callMethodAndCatchErrors(check)) ?? [ null, null ];
if (result != null) {
completed = true;
resolve(result);
return;
}
if (error != null) {
completed = true;
reject(error);
return;
}
if (timeoutMs != null && (Date.now() - start) > timeoutMs) {
completed = true;
let message = typeof timeoutMessage === 'function'
? timeoutMessage()
: timeoutMessage;
reject(new Error(message));
return;
}
}
while (true) {
try {
await tick ();
} finally {}
if (completed === true) {
break;
}
await $promise.wait(intervalMs);
}
});
}
async function callMethodAndCatchErrors (fn: Function): Promise<[Error, any]> {
try {
return await fn();
} catch (error) {
return [ error, null ];
}
}
}