Skip to content

Instantly share code, notes, and snippets.

@softwarespot
Last active May 29, 2018 20:39
Show Gist options
  • Save softwarespot/481e1cfac06ff22ffa297c57bc2a31a3 to your computer and use it in GitHub Desktop.
Save softwarespot/481e1cfac06ff22ffa297c57bc2a31a3 to your computer and use it in GitHub Desktop.
Guard a function and execute a function guarded with optional zone information
// Guard a function, with optional zone information.
// Note: Any function which is guarded inside an already guarded function
// and does not provide any zone information, will inherit from the outer
// guarded function
const defaultName = '<root>';
guard._zone = {
name: defaultName,
// Hidden property, due to being prefixed with "$"
$names: defaultName,
};
function guard(fn, zone = { }) {
// Check if the function is already guarded
// if (fn.guard) {
// return fn.guard;
// }
// Check if the function is a guard function,
// as it will contain the properties "zone" and "previous"
if (fn.zone || fn.previous) {
return fn;
}
guarded.previous = guard.current;
// Clone the zone provided, and transfer properties from the currently active zone
// i.e. if the name is not provided in the provided "zone" argument, then it will be inherited from the currently active zone
guarded.zone = Object.assign({}, guard.current, zone);
if (guard.current.name !== guarded.zone.name) {
// Ensures that "$names" property is private and can't be overridden
guarded.zone.$names = `${guard.current.$names} ${guarded.zone.name}`;
}
// appendStackTrace(guarded.zone);
function guarded(...args) {
guard._zone = guarded.zone;
try {
return fn.apply(guarded.zone.context, args);
} catch (ex) {
if (typeof guard.onHandleError === 'function') {
guard.onHandleError(ex, guarded.zone, guarded.zone.$names);
} else {
console.error(ex, guarded.zone);
}
} finally {
guard._zone = guarded.previous;
}
}
// fn.guard = guarded;
return guarded;
}
Object.defineProperty(guard, 'current', {
get() {
return guard._zone;
}
});
function appendStackTrace(zone) {
try {
// For IE11 only
throw new Error();
} catch (ex) {
zone.stack = ex.stack;
}
}
// Helper function for immediately invoking a function that will be guarded
function runGuarded(fn, args = [], zone = {}) {
return guard(fn, zone)(...args);
}
// Register a global event handler.
// Note: This is optional
guard.onHandleError = (err, zone, names) => {
console.error('Global handler: ', err, ' => ', 'Zone name:', zone.name, 'Zone names:', names);
console.log(zone);
// The current zone should equal the zone which threw an exception
console.log(guard.current === zone);
// console.error(err.stack);
};
// Example(s)
const zone1 = {
name: '<root 1>',
context: { foo: 'bar', },
};
runGuarded(() => {
setTimeout(guard(() => {
throw new Error('An unexpected error occurred in async function (1.1), zone name: <root1>, zone names: <root> <root1>');
}), 100);
throw new Error('An unexpected error occurred (1.0), zone name: <root1>, zone names: <root> <root1>');
}, undefined, zone1);
const zone2 = {
name: '<root 2>',
context: { baz: 'biz', },
};
runGuarded(() => {
setTimeout(guard(() => {
setTimeout(guard(() => {
throw new Error('An unexpected error occurred in async function (2.2), zone name: <root2>, zone names: <root> <root2>');
}), 100);
throw new Error('An unexpected error occurred (2.1), zone name: <root2>, zone names: <root> <root2>');
}), 100);
runGuarded((...args) => {
console.log(args);
setTimeout(guard(() => {
// Guarded async function inside another guarded async function
setTimeout(guard(() => {
throw new Error('An unexpected error occurred in async function (3.2), zone name: <root3>, zone names: <root> <root2> <root3>');
}), 200);
throw new Error('An unexpected error occurred in async function (3.1), zone name: <root3>, zone names: <root> <root2> <root3>');
}), 200);
throw new Error('An unexpected error occurred (3.0), zone name: <root3>, zone names: <root> <root2> <root3>');
}, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], {
name: '<root 3>'
});
throw new Error('An unexpected error occurred (2.0), zone name: <root2>, zone names: <root> <root2>');
}, [], zone2);
runGuarded((...args) => {
console.log(args);
throw new Error('An unexpected error occurred (0.0), zone name: <root>, zone names: <root>');
}, [10, 20, 30, 40, 50, 60, 70, 80, 90])
const origFn = () => {};
const orignFn2 = () => {};
const guardedFn = guard(origFn);
// Check it's not re-guarding a function which is already guarded
console.assert(guardedFn !== guard(origFn));
console.assert(guardedFn === guard(guardedFn));
console.assert(guard(origFn) !== guard(origFn));
console.assert(guard(guardedFn) === guard(guardedFn));
console.assert(guard(orignFn2) !== guard(orignFn2));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment