@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
65 lines (64 loc) • 2.25 kB
JavaScript
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) {
if (!opt.timeout) {
// short-circuit to direct execution if 0 timeout is passed
return await fn();
}
const { timeout, name = fn.name || 'pTimeout function', onTimeout } = opt;
const fakeError = opt.fakeError || new Error('TimeoutError');
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: ok
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);
reject(_errorDataAppend(err, opt.errorData));
}
return;
}
reject(err);
}, timeout);
// Execute the Function
try {
resolve(await fn());
}
catch (err) {
reject(err);
}
finally {
clearTimeout(timer);
}
});
}