Is there a way to 'whitelist' - or prevent - specific errors from being thrown in a try catch block?
For instance, say I have an exception that I know about and don't want to log. However, I want to catch any other errors that pop up in that try/catch block.
This is the best that I can come up with:
try {
...
} catch(exception) {
//Loop through a predefined list of known errors that we want to ignore.
for (var i in whitelistOfErrors) {
//if our exception MATCHES an error that we want to ignore,
//don't throw it
if (exception == whitelistOfErrors[i])
break;
//if our exception DOESN'T MATCH an error that we want to ignore,
//throw it
if (i = whitelistOfErrors.length)
throw exception;
}
}
(or replace the logic with a method that searches the array for you, if that exists)
Related
Here is my scenario:
I am creating a user object which I am saving to my database. After this I am doing something else which may result in an error. If so, I need to "rollback" the changes I made to the database, meaning I have to delete the user object from the database again in the catch block. However, this delete action may also fail meaning I need to know how I handle this?
When I say "handle" what I mean is I would like to save the error to my database. So I want the original error to be saved and also the error in the case the deleting fails. (I also know saving the error to the database might fail, but if it does there isnt much I can do so I'll just let it happen)
So do I need to use a nested try-catch inside the catch block? or will the catch block "catch" its own errors?
// psuedocode-ish illustation of what I'm working with
try {
const new_user = Database.save(user);
MoreCodeThatMightThrowAnError(); // imagine this throws an error
}
catch (error) {
if (new_user) Database.delete(user); // Do I need this inside a nested try-catch?
console.log(error);
Database.save(error); // dont care if this fails
}
Also, this is just a simplified example of what I am doing so I cannot just move the MoreCodeThatMightThrowAnError() up or use some build in rollback functionality from my database unfortunantly.
You are correct, you need to use another try-catch block. Even though it' seems a bit strange, it's sometimes unavoidable. See this question for more.
I would suggest organizing your code like this:
// psuedocode representation
try {
const new_user = Database.save(user);
try {
const otherCode = Database.otherThingThatCauseError();
} catch(err) {
console.log(err)
}
// ... and so on
} catch(err) {
// no need to rollback, most databases are ACID-compliant
console.log(err);
So basically, you would want to add another try, catch block. I believe that you won't have to rollback your changes since databases are ACID compliant, so if something wrong happens in the middle of an operation (i.e. creating a new user), the database will automatically roll back the whole operation.
A catch block does not catch errors that occur inside of it, so you would need to use a nested try...catch statement.
try {
const new_user = Database.save(user);
MoreCodeThatMightThrowAnError(); // imagine this throws an error
}
catch (error) {
console.log(error);
try {
if (new_user) Database.delete(user);
} catch (error2) {
console.log(error2);
try {
Database.save(error2);
} catch (error3) {}
}
}
I'm trying to make it easier to debug when an exception is thrown by eval().
If I simply do this:
let something = 0;
eval("something else");
Then everything would crash, and it says:
undefined: 1
something else
^^^^
SyntaxError: Unexpected token 'else'
{stacktrace}
pointing to whatever the issue was with these ^^^^, along with the stack trace etc.
However, if I put this in a try/catch like this:
try{
let something = 0;
eval("something else");
}catch(e){
console.log(e);
}
Then it prints out:
SyntaxError: Unexpected token 'else'
{stacktrace}
without the nice arrows pointing to the problem in the original evaluated string.
How can I catch the error and make it output the exact same thing as it does without catching it? Is it possible?
I've seen both:
throw new Error(error);
&
console.error(error);
E.G:
jQuery:
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
&
Vue.js:
if (config.warnHandler) {
config.warnHandler.call(null, msg, vm, trace);
} else if (hasConsole && (!config.silent)) {
console.error(("[Vue warn]: " + msg + trace));
}
Both error handling ways seem reliable and used. But my question is:
Is there a difference between them? And if there is, when should I use which?
The key difference: throwing halts the execution, while console.error does not.
Most of the time, it's better to throw an error.
That's a built-in way to signal that something failed and normal execution cannot continue unless the error is expected, caught and handled properly.
In the most platforms, an uncaught exception will be logged to the console as well to warn the developer about it, but caught exceptions won't be logged, since they are assumed to be handled by the code.
Using console.error can be good for cases where the error happened isn't fatal, but you'd like to warn the developer.
However, overusing this feature can easily cause other errors and harder-to-debug code. For example, consider this code:
const elem = document.querySelector('.elem')
if(!elem)
console.error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
console.error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
console.error('Doing something else with elem has failed!')
The above code will log three errors if there's no elem, but execution still continues, maybe causing even more errors.
By throwing an exception, this is avoidable:
const elem = document.querySelector('.elem')
if(!elem)
throw new Error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
throw new Error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
throw new Error('Doing something else with elem has failed!')
This will print only the first error message, and execution halts, unless you put it inside a try..catch structure, like:
try{
const elem = document.querySelector('.elem')
if(!elem)
throw new Error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
throw new Error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
throw new Error('Doing something else with elem has failed!')
}catch(exception){
console.error(exception)
fallbackMethod()
}
There's another difference: thrown errors can be caught by the caller of your function, so it can programmatically handle them (causing the execution to continue, and the error isn't displayed). On the other hand, if you use console.error, the caller can not decide if an error is expected, causing to log the error even if it is normal, so the console can become messy (you can't decide what's a real error and what's not.
Those two are completely different, let's see documentation of each one:
console.error:
Outputs an error message to the Web Console.
Throw:
throws a user-defined exception. Execution of the current function
will stop (the statements after throw won't be executed), and control
will be passed to the first catch block in the call stack. If no catch
block exists among caller functions, the program will terminate.
So, as you can see, they work differently, you can use both in your code, so keep in mind that just a console.error will probably pass unseen by the user, only those with console open will see the message, while throw will be noted, since everything will stop.
Let's see some functional examples:
console.error
Note that the "custom" error will show on console, but code stills executing, you only see the error message because console is showing here in stackOverflow. To stop execution you would need a return right after the console.error
function changeString(currentString, append) {
if (typeof append != 'string') {
console.error("Cannot change string!");
}
//THIS PART BELOW WILL EXECUTE, even with error logged
console.log(currentString + append);
}
changeString("I will show up even with error logged ", null)
Throw
Note that code stops execution when error is thrown and also the message of the error is different from the one that console.error shows.
function changeString(currentString, append){
if (typeof append != 'string'){
throw new Error("Cannot change string!");
}
//THIS PART BELOW WONT EXECUTE
console.log(currentString + append);
}
changeString("I wont show up in the console", null)
Here I added a try/catch, note the catch block being executed, this way I have control over the error and what should happen after the error being thrown, also, I mixed both: Throw and console.error
function changeString(currentString, append) {
try {
if (typeof append != 'string') {
throw new Error("Cannot change string!");
}
console.log(currentString + append);
} catch (er){
console.error("I'm being logged from inside a catch block because an error was thrown");
//DO WHATEVER YOU NEED TO FIX THE ERROR AND ALERT THE USER HERE
}
}
changeString("I wont show up in the console", null)
Further read on official documentations:
Throw
console.error
I would not consider them to be interchangeable. Console.error() simply logs your error to the console with special formatting.
When you actually throw an error you are essentially terminating execution unless you catch the error using a try catch block or some other form of error handling.
Why not have both, like this:
throw new ConsoleError('bad thing');
Source:
// based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
class ConsoleError extends Error {
constructor(...args) {
const returnValue = super(...args);
console.error(...args);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ConsoleError);
}
this.name = 'ConsoleError';
this.message = args[0];
// Pass remaining arguments (including vendor specific ones) to parent constructor
return returnValue;
}
}
window.ConsoleError = ConsoleError;
You may also want show the user when some errors happen, for this, use
// this to avoid crashing:
window.toastError('nice user error message', 'extra developer data:', someObject)
// this to crash:
throw new ToastError('we made a mistake')
Source:
window.toastError = (userMsg, ...developerDebugArgs) => {
// TODO: implement toast notifications
// only use userMsg for the toast notification
// Using DOMPurify and rendering userMsg as (potential) html may be nice,
// but backend team may prefer to simple return plain text instead of html
// may need to de-dupe messages being sent to toast component
console.log(
`%c${userMsg}`,
'color: orangered;font-style:italic;',
...developerDebugArgs
);
};
class ToastError extends Error {
constructor(...args) {
const returnValue = super(...args);
window.toastError(...args);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ToastError);
}
this.name = 'ToastError';
this.message = args[0];
// Pass remaining arguments (including vendor specific ones) to parent constructor
return returnValue;
}
}
window.ToastError = ToastError;
You could also rename the ToastError class ToastException instead, if it suits your fancy
I'm using defiantjs to perform json searches.
result = JSON.search(data, '//*[name="Gheorghe"]');
If the value is not found in the json I get an error Cannot read property 'Gheorghe' of undefined.
Is there a way to return something else if no search string is matched?
try {
//check for return value (search result or falsy value)
result = JSON.search(data, '//*searchValue') || 'not found';
} catch (e) {
//if there was an error in the request
console.log(e);
result = 'Not found. But dude, we could not even finish your request, as we encountered the following error: ' + e;
}
You can use try-catch statement
The try...catch statement marks a block of statements to try, and specifies a response, should an exception be thrown.
Example
In the following example, code in the try block can potentially throw three exceptions: TypeError, RangeError, and EvalError (Error types: EvalError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError). When an exception occurs, control transfers to the appropriate catch clause. If the exception is not one of the specified exceptions and an unconditional catch clause is found, control transfers to that catch clause.
var result;
try{
alert(result.id);
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
e='This is my custom type-error message instead of "Cannot read property..."';
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
e='This is my custom range-error message instead of "Cannot read property..."';
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
e='This is my custom eval-error message instead of "Cannot read property..."';
} else {
// statements to handle any unspecified exceptions
e='This is my custom unspecified-error message instead of "Cannot read property..."';
}
console.log("Error!",e); // prints to console custom error message
}
alert(result.id); // prints to console default error message result is undefined
Why can't you use
if(typeof(JSON.search(data, '//*[name="Gheorghe"]')) !== 'undefined') {
result = JSON.search(data, '//*[name="Gheorghe"]');
}
else {
//something else
}
Defiant.js, the third party library is supposed to take care of this issue. I take your word that they are not.
In that case, there are two ways to solve this issue.
To modify the library to return undefined when no results are found - this way is not recommended as it will be problematic to other developers who are working on the code base and also to maintain versioning of the library using bower/npm.
To use try catch around the implementation - this is the ideal way to solve this problem as you don't have to make changes to the third party library and you can get an easy trigger to check if the error from the try block is equal to "Cannot read property 'Gheorghe' of undefined", you can run your preferred callback.
This is the solution I would recommend.
var name = "Gheorge";
try {
result = JSON.search(data, '//*[name="' + name + '"]');
}
catch (error === "Cannot read property '" + name + "' of undefined") {
// As there could be any type of error from the function JSON.search
console.log(error);
yourErrorHandler(error);
}
UPDATE
[Rewriting question to focus on the problem I am trying to understand.]
Is there a means in JavaScript to throw Exceptions that notify the line number where the problem occurs? Similar to C#'s debugger, if an error is thrown on line 50 then I will be taken to line 50.
For example, according to MDN EvalError represents an error with eval(). So, let's say I have a function that uses eval(). I want to use a specific error that is representative of the problem at hand, EvalError:
//As written here the error implies there is a problem on this line. See Firebug console window
var evalErra = new EvalError('required element missing from evaluation');
var stringFunc = "a=2;y=3;document.write(x*y);";
EvalString(stringFunc);
function EvalString(stringObject) {
//Some arbitrary check, for arguments sake let's say checking for 'x' makes this eval() valid.
if(stringObject.indexOf('x') !== -1) {
throw evalErra;
//throw 'required element missing from evaluation';//This way offers no line number
}
eval(stringFunc);//The problem really lies in the context of this function.
}
If I'm going about this all wrong, then please tell me how I should approach these kinds of issues.
When you throw an error, execution of the current code will stop and JS will work its way back up the execution tree until it finds a catch () which handles the particular type of error being thrown, or gets all the way up to the top of the tree, causing an "unhandled exception" error: You threw an error, and nothing caught it, and now someone's window got broken.
try {
if (true) {
throw 'yup'
}
} catch (e) { // catches all errors
... handle the error
}
When doing error handling you want to do the following
throw new Error("message");
Then if you ever handle this error look at err.stack (firefox/opera/chrome) or err.line (Safari) or err.IE_Y_U_NO_SHOW_ME_ERROR_LINE_NUMBER (IE) to find the line number.
If you want you can subclass Error.