method babel decorators isn't executed - javascript

I'm using the babel 7 decorator plugin and I have a simple class and I want to decorate each method in a simple try catch wrapper.
This is what've done:
const errorHandler = () => {
return (target, property, descriptor) => {
try {
return descriptor
} catch (e) {
console.error('error from the decorator', e)
}
}
}
In this is a sample of my class:
class Example {
#errorHandler
addComponent() {
throw new Error('Error')
}
}
But when I'm executing the function it's not going throw the decorator before execution, only pre-evaluating when the class is being initialized.
any ideas?

You are returning descriptor, which is a function object and it gets executed by caller outside your try/catch block. To intercept exception - you should execute descriptor yourself.
The correct code is:
const errorHandler = (target, property, descriptor) => {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = async function(...args) {
try {
return await original.apply(this, args);
} catch (e) {
console.error('error from the decorator', e)
}
}
}
}
class Example {
#errorHandler
addComponent() {
throw new Error('Error')
}
}
new Example().addComponent();

Related

Error handling inside addEventListener callback

How do developers structure their programs if they want to have a top-level error handling function?
The immediate thought that came into my mind was to wrap a try..catch to the main function, however, this does not trigger errors from callbacks?
try {
main();
} catch(error) {
alert(error)
}
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
<button>
Click me to see my callback error
</button>
Try-catch functionality around already existing functions/methods gets achieved best by wrapper approaches.
For the OP's use case one needs a modifying wrapper function which explicitly targets the handling of "after throwing" ...
// - try-catch wrapper which specifically
// targets the handling of "after throwing".
function afterThrowingModifier(proceed, handler, target) {
return function (...argsArray) {
let result;
try {
result = proceed.apply(target, argsArray);
} catch (exception) {
result = handler.call(target, exception, argsArray);
}
return result;
}
}
function failingClickHandler(/* event */) {
throw new Error('Error from click callback');
}
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', afterThrowingModifier(
failingClickHandler, afterTrowingHandler
));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
One of cause can implement prototype based abstractions for a function modifying failure handling like afterThrowing or afterFinally. Then the above main example code changes to something more expressive like ...
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', (function (/* event */) {
throw new Error('Error from click callback');
}).afterThrowing(afterTrowingHandler));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
<script>
(function (Function) {
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function afterThrowing/*Modifier*/(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterThrowingType(...argumentArray) {
const context = getSanitizedTarget(this) ?? target;
let result;
try {
// try the invocation of the original function.
result = proceed.apply(context, argumentArray);
} catch (exception) {
result = handler.call(context, exception, argumentArray);
}
return result;
}
) || proceed;
}
// afterThrowing.toString = () => 'afterThrowing() { [native code] }';
Object.defineProperty(Function.prototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing/*Modifier*/
});
}(Function));
</script>
In javascript you can override global onerror, catching most of the errors:
window.onerror = function(message, source, lineno, colno, error) { ... };
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
In your case:
window.onerror = function(message, source, lineno, colno, error) {
console.error(message);
alert(message);
return false
};
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
main();
Some extra info:
https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
Added after questions if Promise would raise the error, lets test:
window.onerror = (message, source, lineno,colno,error)=>{
console.error(`It does!, ${message}`);
};
const aFn = ()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
throw new Error("whoops")
}, 3000);
});
}
aFn();
Result:
VM1163:2 It does!, Script error.
window.onerror # VM1163:2
error (asynchroon)
(anoniem) # VM1163:1
VM1163:7 Uncaught Error: whoops
at <anonymous>:7:19

How to implement a simpler Promise in JavaScript?

I'm learning JavaScript, and I decided that an excelent chalenge would be to implement a custom Promise class in JavaScript. I managed to implement the method then, and it works just fine, but I'm having difficulties with the error handling and the method catch. Here is my code for the Promise class (in a module called Promise.mjs):
export default class _Promise {
constructor(executor) {
if (executor && executor instanceof Function) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
}
resolve() {
if (this.callback && this.callback instanceof Function) {
return this.callback(...arguments);
}
}
reject(error) {
if (this.errorCallback && this.errorCallback instanceof Function) {
return this.errorCallback(error);
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
}
then(callback) {
this.callback = callback;
return this;
}
catch(errorCallback) {
this.errorCallback = errorCallback;
return this;
}
}
When I import and use this class in the following code, all the then() clauses run as according, and I get the desired result in the console:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
But, when I add an invalid argument to the sum() function, i.e. not a number, the reject() method runs, but it don't stop the then() chain, as should be, and we also get an exception. This can be seen from the following code:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, '5').then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
Also, if I catch an error in nested then() methods, the outer catch() doesn't notice this and I get an exception again. The goal is to implement a lightweight functional version of Promises, but not necessarily with all its functionality. Could you help me?
The problem in your code is that your sum function calls both the reject and the resolve functions. There's no handling in the sum function that will cause it not to call the resolve function at the end, and there is nothing in your _Promise that blocks this behavior.
You have 2 options to fix this.
Option 1 would be if you want your _Promise to act like a real Promise you will need to manage a state and once a promise got to a final state stop calling the callback or errorCallback.
Option 2 would be to prevent from calling both reject and resolve in the function calling the _Promise, in this case, the sum function.
With the comments that you guys provide me, I was able to improve the code and correct the errors mentioned, as shown below. Now, I would like you to give me suggestions on how to proceed and improve the code. Thanks. (The code can also be found on github).
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function _Promise(executor) {
let state = PENDING;
let callOnFulfilled = [];
let callOnRejected = undefined;;
function resolve(...args) {
if (!state) {
state = FULFILLED;
}
resolveCallbacks(...args);
};
function reject(error) {
state = REJECTED;
if (callOnRejected && (callOnRejected instanceof Function)) {
callOnRejected(error);
callOnRejected = undefined;
callOnFulfilled = [];
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
};
function resolveCallbacks(...value) {
if (state !== REJECTED) {
let callback = undefined;
do {
callback = callOnFulfilled.shift();
if (callback && (callback instanceof Function)) {
const result = callback(...value);
if (result instanceof _Promise) {
result.then(resolveCallbacks, reject);
return;
} else {
value = [result];
}
}
} while (callback);
}
};
if (executor && (executor instanceof Function)) {
executor(resolve, reject);
}
this.then = function (onFulfilled, onRejected) {
if (onFulfilled) {
callOnFulfilled.push(onFulfilled);
if (state === FULFILLED) {
resolveCallbacks();
}
}
if (onRejected && !callOnRejected) {
callOnRejected = onRejected;
}
return this;
};
this.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
}
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
return 25;
});
}).then(function (value) {
console.log(value);
console.timeEnd('codeExecution');
});
}).catch(function (error) {
console.log(error);
});

Angular method decorator error handling with subscriptions

Currently, I have an angular application with a method decorator to handle errors of all the methods in the component. It catches all the errors from methods, But it is unable to catch the error inside subscribe. Any suggestions to do this?
This is my current code.
This is the sampling method I want to catch the errors
#logActionErrors()
getEmailSettings() {
this.sharedService.getSMTPConfigurations().subscribe(() => {
throw('this is an error');
}, (ex) =>{
console.log(ex);
})
}
This is my Method Decorator
export function logActionErrors(): any {
return function (target: Function, methodName: string, descriptor: any) {
const method = descriptor.value;
descriptor.value = function (...args: any[]) {
try {
let result = method.apply(this, args);
// Check if method is asynchronous
if (result && result instanceof Promise) {
// Return promise
return result.catch((error: any) => {
handleError(error, methodName, args, target.constructor.name);
});
}
if(result && result instanceof Observable ){
console.log(methodName);
result.pipe(catchError((error: any) => {
console.log(error);
handleError(error, methodName, args, this.constructor.name);
return result
}))
}
// Return actual result
return result;
} catch (error:any) {
handleError(error, methodName, args, target.constructor.name);
}
}
return descriptor;
}
}
I want to catch this throw('this is an error'); error on this sample. any suggestions to do this?

Javascript ES5/ES6 classes and error handling

Say I have a class like this
class SomeUIComponentDataStore {
async function getUser() {
try { //do something that can fail}
catch(e) {
// gracefully fail, setting portion of ui to fail state
Sentry.captureException(e); // report to some metrics service
}
}
}
I repeat that pattern for every async function. Where on failure I respond to the error, and then report it to some service (in this case that service is Sentry).
Is there anyway I can create a BaseClass, that will automatically decorate my catch statement with Sentry.caputreException(). Or do i have to manually write it each time a I see an error.
You could define a decorator to reuse that logic and decorate methods that can throw:
function catchError(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
try {
return original.apply(this, args);
} catch (e) {
Sentry.captureException(e); // report to some metrics service
}
}
}
}
function catchErrorAsync(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = async function(...args) {
try {
return await original.apply(this, args);
} catch (e) {
Sentry.captureException(e); // report to some metrics service
}
}
}
}
class SomeUIComponentDataStore {
#catchErrorAsync
async getUser() {
//do something that can fail
}
#catchError
otherMethod() {
//do something that can fail
}
}
You could create a base class with the Sentry.captureException(e);, and then have overrideable functions for the custom try/catch functionality.
class BaseClass {
function onGetUser() {
throw new Error("Method not implemented");
}
function onGetUserFail() {
throw new Error("Method not implemented");
}
async function getUser() {
try {
onGetUser();
} catch (e) {
onGetUserFail();
Sentry.captureException(e);
}
}
}
class SomeUIComponentDataStore extends BaseClass {
function onGetUser() {
// do something
}
function onGetUserFail() {
// do something
}
}

decorators: "this" is undefined when accessed in descriptor.value

I am trying out decorators, I have written a decorator that basically returns a new function that does some `console.log.
This is what my decorator looks like:
function test(target, name, descriptor) {
const original = descriptor.value;
console.log("bbau");
if (typeof original === 'function') {
descriptor.value = function (...args) {
console.log(`Arguments: ${args}`);
try {
console.log("executing");
const result = original.apply(this, args);
console.log("done");
console.log(`Result: ${result}`);
return result;
} catch (e) {
console.log(`Error: ${e}`);
throw e;
}
}
}
return descriptor;
}
And this is how I am using it:
class TestController extends BaseController<//..> {
// ...
#test
testIt(req: Request, res: Response) : Response {
this.sendResponse();
}
sendResponse(options: ISendResponseOptions, res: Response) : Response {
// return response
}
}
``
However, when executed an error is raised: Error: TypeError: Cannot read property 'sendResponse' of undefined.
Any thoughts about what it could be? Thanks!
You should generally use an arrow function when you want to capture this from the context you declared the function in (or when this does not matter). In this case you really want this to be the object the function was called on so you should use a regular function :
const test = (target, name, descriptor) => {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function (...args) {
console.log(`Arguments: ${args}`);
try {
console.log("executing");
const result = original.apply(this, args);
console.log("done");
console.log(`Result: ${result}`);
return result;
} catch (e) {
console.log(`Error: ${e}`);
throw e;
}
}
}
return descriptor;
}
You can test it out in the playground
If you use this function as a parameter to another function you should also call bind to set this for the function (otherwise the caller will determine the value of this):
router.route("/").post(testController.testIt.bind(testController))

Categories

Resources