UNPKG

@naturalcycles/js-lib

Version:

Standard library for universal (browser + Node.js) javascript

69 lines (68 loc) 2.41 kB
import { _errorDataAppend, TimeoutError } from '../error/error.util.js'; import { _typeCast } from '../types.js'; /** * Decorates a Function with a timeout. * Returns a decorated Function. * * Throws an Error if the Function is not resolved in a certain time. * If the Function rejects - passes this rejection further. */ export function pTimeoutFn(fn, opt) { opt.name ||= fn.name; if (!opt.timeout) { return fn; } return async function pTimeoutInternalFn(...args) { return await pTimeout(() => fn.apply(this, args), opt); }; } /** * Decorates a Function with a timeout and immediately calls it. * * Throws an Error if the Function is not resolved in a certain time. * If the Function rejects - passes this rejection further. */ export async function pTimeout(fn, opt) { const ac = new AbortController(); const { signal } = ac; if (!opt.timeout) { // short-circuit to direct execution if 0 timeout is passed return await fn(signal); } const { timeout, name = fn.name || 'pTimeout function', onTimeout } = opt; const fakeError = opt.fakeError || new Error('TimeoutError'); return await new Promise(async (resolve, reject) => { // Prepare the timeout timer const timer = setTimeout(() => { const err = new TimeoutError(`"${name}" timed out after ${timeout} ms`, opt.errorData); // keep original stack err.stack = fakeError.stack.replace('Error: TimeoutError', 'TimeoutError: ' + err.message); if (onTimeout) { try { resolve(onTimeout(err)); } catch (err) { _typeCast(err); // keep original stack err.stack = fakeError.stack.replace('Error: TimeoutError', err.name + ': ' + err.message); // oxlint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(_errorDataAppend(err, opt.errorData)); } ac.abort(err); return; } reject(err); ac.abort(err); }, timeout); // Execute the Function try { resolve(await fn(signal)); } catch (err) { reject(err); } finally { clearTimeout(timer); } }); }