2 min readNov 25, 2024
A generic implementation for safely executing any kind of function in its best possible way should be served similar to e.g. call
/ apply
/ bind
as method of Function.prototype
respectively AsyncFunction.prototype
...
function exposeInternalType(value) {
return Object.prototype.toString.call(value);
}
function isAsyncFuntionType(value) {
return exposeInternalType(value) === '[object AsyncFunction]';
}
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function execute/*Safely*/(target, ...args) {
const proceed = this;
let result = null;
let error = null;
if (isFunction(proceed)) {
if (isAsyncFuntionType(proceed)) {
error = new TypeError(
'The non-async `execute` exclusively can be invoked at non-async callable types.'
);
} else {
try {
result = proceed.apply(target ?? null, args);
} catch (/** @type {Error} */exception) {
error = exception;
}
}
} else {
error = new TypeError(
'`execute` exclusively can be invoked at a callable type.'
);
}
return [/** @type {null|Error} */error, result];
}
async function executeAsync/*Safely*/(target, ...args) {
const proceed = this;
let result = null;
let reason = null;
let error = null;
if (isFunction(proceed)) {
try {
result = await proceed.apply(target ?? null, args);
} catch (/** @type {string|Error} */exception) {
reason = exception;
}
} else {
error = new TypeError(
'`execute` exclusively can be invoked at a callable type.'
);
}
return [/** @type {null|string|Error} */reason ?? error, result];
}
Reflect.defineProperty(Function.prototype, 'execute', {
value: execute,
writable: true,
configurable: true,
});
Reflect.defineProperty((async function () {}).constructor.prototype, 'execute', {
value: executeAsync,
writable: true,
configurable: true,
});
function eitherThrowOrNot() {
const value = Math.random();
if (Math.floor(value + .5) === 1) {
throw new Error;
} else {
return value;
}
}
async function eitherThrowOrNotAsync() {
return new Promise(resolve =>
setTimeout(resolve, 500, eitherThrowOrNot()),
);
}
let [error, result] = await eitherThrowOrNotAsync.execute();
console.log({ error, result });
([error, result] = eitherThrowOrNot.execute());
console.log({ error, result });