how to throw error for destructuring non-existent key - javascript

So I'd like to destructure an object and have it throw an error if one of the keys didn't exist. I tried a try catch but it didn't work. And I'd like an alternative to just if (variable === undefined)
let obj = {test : "test"}
try {
let { test, asdf } = obj
console.log("worked")
}
catch (error) {
console.error(error)
}

Use proxy to throw the error if a non-existent property is fetched
let obj = {
test: "test"
};
try
{
let { test, asdf} = getProxy(obj);
console.log("worked");
}
catch (error)
{
console.error(error)
}
function getProxy(obj) {
return new Proxy(obj, { //create new Proxy object
get: function(obj, prop) { //define get trap
if( prop in obj )
{
return obj[prop];
}
else
{
throw new Error("No such property exists"); //throw error if prop doesn't exists
}
}
});
}
Demo
let obj = {
test: "test"
};
try
{
let { test, asdf} = getProxy(obj);
console.log("worked");
}
catch (error)
{
console.error(error)
}
function getProxy(obj) {
return new Proxy(obj, {
get: function(obj, prop) {
if( prop in obj )
{
return obj[prop];
}
else
{
throw new Error("No such property exists");
}
}
});
}

The try-catch work for the runtime errors, catching exceptions or error you throw explicitly. Thus, in destructuring, there is no such error thrown when the matching key is not found. To check the existence you need to explicitly create a check for it. Something like this,
let obj = {test : "test"}
let { test, asdf } = obj
if(test){
console.log('worked');
} else {
console.log('not worked');
}
if(asdf){
console.log('worked');
} else {
console.log('not worked');
}
This is because the destructuring works the same way as we assign the object value to another value like,
let obj = {test : "test"}
var test = obj.test;
var asdf = obj.asdf;
Here, doing obj.asdf will give you undefined in asdf and does not throw any exception. Thus, you cannot use try-catch for that.

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

handling multiple errors in Node js

I have a method in Node JS which reads a file containing JSON data and finds a product with specific ID.
async getProductwithId(id) {
try {
let rawData = fs.readFileSync("data/products.json");
let data = JSON.parse(rawData);
for (const element of data) {
if (id === element.productId) {
return element;
}
}
throw new ProductDoesNotExistError("No Such Product Exists");
} catch (error) {
throw new FileReadingError("Error Reading File");
}
}
where ProductDoesNotExistError and FileReadingError both extend Error. I have put try/catch for the fs.readFileSync()
the problem is even if i have ProductDoesNotExistError, it's sending FileReadingError. I want to handle here the FileReadingError only and not ProductDoesNotExistError. I will let the callling function handle the ProductDoesNotExistError. How do I achieve this functionality.
Since in your catch block you throw a new instance of FileReadingError, all caught errors will result in the latter. You could either put the try/catch just around the readFileSync operation or check the type of the error in your catch block (also there's no need for async as the code inside the method is not async - e.g. your not using fs.promises.readFile()):
getProductwithId(id) {
let rawData;
try {
rawData = fs.readFileSync("data/products.json");
} catch (error) {
throw new FileReadingError("Error Reading File");
}
const data = JSON.parse(rawData);
for (const element of data) {
if (id === element.productId) {
return element;
}
}
throw new ProductDoesNotExistError("No Such Product Exists");
}
or you do:
getProductwithId(id) {
try {
const rawData = fs.readFileSync("data/products.json");
const data = JSON.parse(rawData);
for (const element of data) {
if (id === element.productId) {
return element;
}
}
throw new ProductDoesNotExistError("No Such Product Exists");
} catch (error) {
if (error instanceof ProductDoesNotExistError) {
// rethrow ProductDoesNotExistError error
throw error;
}
throw new FileReadingError("Error Reading File");
}
}

Failing test - Mocha's done() called multiple times

I've tried looking at topics with a similar error but could not fit those solutions into the context of my issue.
When I try to run the the following test (function included that is tested):
function myFunc(next, obj) {
const pairs = {};
obj.listing.forEach((element) => {
if (element.x in pairs && pairs[element.x] !== element.y) {
const err = new Error('This was not ok');
next(err);
} else {
pairs[element.x] = element.y;
}
});
next();
}
it('should fail as 9 has been changed to 5 in the second object of the listing', function (done) {
const callback = (err) => {
if (err && err instanceof Error && err.message === 'This was not ok') {
// test passed, called with an Error arg
done();
} else {
// force fail the test, the `err` is not what we expect it to be
done(new Error('Assertion failed'));
}
}
myFunc(callback, {
"listing": [
{ "x": 5, "y": 9 },
{ "x": 5, "y": 11 }
]
});
});
I get this error:
What is the cause of this and how can I fix it?
You need to add a return in the if block of your myFunc so that the callback function next is called only once and indeed the done() callback in the main test case:
function myFunc(next, obj) {
const pairs = {};
obj.listing.forEach((element) => {
if (element.x in pairs && pairs[element.x] !== element.y) {
const err = new Error('This was not ok');
return next(err);
} else {
pairs[element.x] = element.y;
}
});
next();
}
#Ankif Agarwal's solution was not the correct one but it did point me in the right direction.
The forEach() method is not short circuited and therefor makes a call to next() more than once (Short circuit Array.forEach like calling break).
I was able to solve this in one of two way's.
By extracting the call to next() from the forEach() logic:
function myFunc(next, obj) {
const pairs = {};
let err = null;
obj.listing.forEach((element) => {
if (element.x in pairs && pairs[element.x] !== element.y) {
err = new Error('This was not ok');
} else {
pairs[element.x] = element.y;
}
});
if (err !== null) {
next(err);
} else {
next();
}
}
However this still makes the forEach() run through all element. If possible it seems better to short circuit it and break out of it soon as a violation occurs that sets the error, like so:
function myFunc(next, obj) {
const pairs = {};
const BreakException = {};
let err = null;
try {
obj.listing.forEach((element) => {
if (element.x in pairs && pairs[element.x] !== element.y) {
err = new Error('This was not ok');
throw BreakException;
} else {
pairs[element.x] = element.y;
}
});
next();
} catch (e) {
if (e !== BreakException) throw e;
next(err);
}
}
Hopefully someone can use this in the future.

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))

how to use js Promise to eliminate pyramid of doom

i'm trying to understand how to use js Promise api to refractor a code that has lots of nested IF.
example when getting JSON object from localstorage a normal code would look like
function $storage(key,default) {
let json = localStorage.getItem(key);
if(json === null) return default;
try{ // <-- need try catch in case value was not valid json object
json = JSON.parse(json);
} catch (e) {
json = default;
}
return typeof json === 'object' ? json : default;
}
the readibility of this code is not that good. so i thought may be i can utilize js Promise to rewrite it into
function $storage (key, default) {
let ret;
let promise = new Promise( (y,n) => y(localStorage) )
.then( ls => JSON.parse(ls.getItem(key)) )
.then( json => typeof json === 'object' ? json : HOW_TO_THROW_ERROR() )
//on more validation step if needed
.then( json => typeof json === 'object' ? json : HOW_TO_THROW_ERROR() )
.then( valid_json => { return = valid_json } )
.catch( error => { ret = default; console.warn('json invalid',e); } );
return ret;
}
now i want to know how can i throw an exception inside then so that the catch can caught it and execute default ?
is this valid usage of js promise of am i wasting performance
You could use Promise.reject() to throw an error:
function $storage (key, default) {
let ret;
let promise = new Promise( (y,n) => y(localStorage) )
.then( ls => JSON.parse(ls.getItem(key)) )
.then( json => typeof json === 'object' ? json : Promise.reject("invalid json") )
.then( valid_json => { return = valid_json } )
.catch( err => { ret = default; console.warn(err.message); } );
return ret;
}
Although I find the following more legible and idiomatic.
function $storage(key,default) {
let json = localStorage.getItem(key);
if(json === null || typeof json !== 'object') json = default;
try{
json = JSON.parse(json);
} catch (e) {
json = default;
} finally {
return json
}
}
Promises are used, as you surely know, for asynchronous computation. Any other use might confuse other programmers.
You can use thrown to thrown the errors and then handle them in catch method
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success!"
throw 'oh, no!';
}).catch(function(e) {
console.log(e); // "oh, no!"
}).then(function(){
console.log('after a catch the chain is restored');
}, function () {
console.log('Not fired due to the catch');
});
// The following behaves the same as above
p1.then(function(value) {
console.log(value); // "Success!"
return Promise.reject('oh, no!');
}).catch(function(e) {
console.log(e); // "oh, no!"
}).then(function(){
console.log('after a catch the chain is restored');
}, function () {
console.log('Not fired due to the catch');
});
But if thrown some errors in async functions the catch is never called.
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // This is never called
});
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
in JavaScript you can use the keyword throw to throw any error.
Examples from MDN:
throw "Error2"; // generates an exception with a string value
throw 42; // generates an exception with the value 42
throw true; // generates an exception with the value true
throw new Error("Error");
function $storage (key, default) {
let ret;
let promise = new Promise( (y,n) => y(localStorage) )
.then( ls => JSON.parse(ls.getItem(key)) )
.then( json => typeof json === 'object' ? json : throw new Error("invalid json") )
//on more validation step if needed
.then( json => typeof json === 'object' ? json : throw new Error("invalid json") )
.then( valid_json => { return = valid_json } )
.catch( err => { ret = default; console.warn(err.message); } );
return ret;
}
You could basically just do the following, because if the parse fails, it will be catched automatically.
function $storage (key, default) {
let ret;
let promise = new Promise( (y,n) => y(localStorage) )
.then(ls => JSON.parse(ls.getItem(key)) )
.then(valid_json => { return = valid_json } )
.catch(err => { ret = default; console.warn(err.message); } );
return ret;
}
The problem I see is just about JSON.parse, wrapping it in a more usable function you get something like:
function safeParse(x) {
try {
return JSON.parse(x);
} catch(e) {
// Log the problem
return null;
}
}
function parmval(key, defval) {
var json = safeParse(localStorage.get(key));
return (typeof json === "object") ? json : defval;
}
Promises are about asynchronous operations, not IFs.

Categories

Resources