UNPKG

0xweb

Version:

Contract package manager and other web3 tools

135 lines (119 loc) 4.32 kB
import { $promise } from './$promise'; export namespace $fn { export function retriable<T extends TRetriableMethod> (fn: T, ctx: any) { return new Retriable(fn, ctx); } export function timeoutPromise<T extends Promise<any>> (promise: T, ms: number): T { let err = new Error(`Promise timeouted in ${ms}ms`) 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 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 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); } }); } type TRetriableMethod = (...args) => Promise<any> interface IRetriableOptions<T extends TRetriableMethod> { timeout?: number retries?: number onError?: (error, ...args) => Promise<Parameters<T>> | void } class Retriable<T extends TRetriableMethod> { private _retries: number = 0; private _options: IRetriableOptions<T> = { timeout: null as number, retries: null as number, onError: null as (error, ...args) => Promise<Parameters<T>> } constructor (public fn: T, public ctx) { } options (opts: IRetriableOptions<T>): this { this._options = opts; return this; } call(...args: Parameters<T>): ReturnType<T> { return this.tick(...args); } private async tick (...args: Parameters<T>) { try { let result = await this.fn.apply(this.ctx, args); return result; } catch (error) { let { timeout, retries = 3, onError } = this._options; if (timeout != null) { await $promise.wait(timeout); } if (++this._retries > retries) { throw error; } if (onError) { error.message += ` Will be re-executed ${this._retries}/${retries}`; let overriddenArgs = await onError(error); if (Array.isArray(overriddenArgs)) { args = overriddenArgs; } } return this.tick(...args); } } } }