Should I use console.error() or throw new Error() - javascript

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

Related

Defiantjs if no results

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

Instantiate JavaScript Error object instance

I'm slowly working my way to understanding the subtleties of JavaScript. At this time, I'm trying to instantiate an instance of the JavaScript Error object. I know that I can do the following:
var error = new Error('The value is invalid');
error.name = 'ArgumentException';
throw error;
However, I'm trying to figure out/understand if there's a way to condense this instantiation down to a single line. For instance, I'd like to do something like the following:
var error = new Error().{ message:'The value is invalid', name:'ArgumentException'};
throw error;
Is it possible to instantiate an instance of the JavaScript Error object while setting both the message and name properties?
Thank you!
You can assign error.name on the same line as throwing it.
If the right hand side of an assignment is a truthy value, which yours is (a non-empty string), the assignment statement will be truthy, meaning you can run on with &&. The last truthy statement will be sent back to throw at the beginning of the line and will be thrown.
var error = new Error('The value is invalid');
throw (error.name = 'ArgumentException') && error;
Without creating your own Error class with name set to ArgumentException, this is as few statements as you can get to without using an Immediately Invoked Function Expression (IIFE) to have one block statement.
EDIT: adeneo correctly used an IIFE which is one statement, even though it contains multiple statements within it as a block. I've edited my last paragraph to include this.
If you really want everything on a single line, you can do something like
var error = Object.defineProperty(new Error('The value is invalid'), 'name', {value: 'ArgumentException'});
You could also throw it on the same line
throw Object.defineProperty(new Error('The value is invalid'), 'name', {value: 'ArgumentException'});
I'm not really fond of how it looks like, but I guess this answers your question.
I created a custom Error object a while ago following along Mozilla recommendations. Basically it creates an object in an OO fashion (pay attention to prototype and constructor). Caution there are some limitations (type-specific catches; read the resources I mentioned below):
// Create a new object, that prototypilly inherits from the Error constructor.
function MyError(message) {
this.name = "MyError";
this.message = message || "Default Message";
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
try {
throw new MyError();
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "Default Message"
}
try {
throw new MyError("custom message");
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "custom message"
}
For further information:
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Error
The Mozilla page refers to the following question (and caveats you must be aware of):
What's a good way to extend Error in JavaScript?

Proper non-string Javascript exceptions

Somehow this does not feel like the culmination of the 50 years programming language development:
throw "My exception message here";
What's the correct way to do exceptions in Javascript, so that
They can be identified (instanceof)
They can carry other payload besides the default message and stack trace
They "subclass" base Exception, so that debug console and such can extract meaningful information about the exception
Possible nested exceptions (converting exception to another): if you need to catch an exception and rethrow new one the orignal stack trace would be preserved and could be meaningfully read by debugging tools
They follow Javascript best practices
throw new Error("message");
or if you want to be more specific use one of the Error Objects
It's important to make sure you throw real errors because they contain the stack trace. Throwing a string is stupid because it doesn't have any meta data attached to it.
You can also subclass errors
// for some sensible implementation of extend
// https://gist.github.com/1441105#file_1pd.js
var MyError = extend(Object.create(Error.prototype), {
...
});
A base "exception" in JavaScript is built-in Error object:
throw new Error("My exception message here");
You can define your custom exceptions as:
function CustomError(message) {
this.message = message;
}
CustomError.prototype = new Error();
CustomError.prototype.constructor = CustomError;
Check for exception type with instanceof. There is also a handy list of built-in exceptions.
You can't do it, and satisfy the question requirements.
Why?
The problem is getting the stack. Native error object (NativeError from now on), initializes the stack when its constructor is called. To get the stack in MyError (your error subclass), you have to call the NativeConstructor inside MyError's constructor. So, your MyError class looks something like this:
function MyError(msg) {
Error.call(this, msg);
}
But, this does not work. Because according to HTML5 spec:
"if you call Error as a function, it returns a new Error object".
Instead of Error's constructor being nice and initializing your custom error class, it creates an error object that you have no use for. I haven't found a way to call NativeError constructor and have it initialize MyError subclass.
Not all is lost.
If we relax the original question requirements, and forget about "instanceof" to test exception class (so Java anyway), and use "name" instead, it can be done. Just declare MyError as:
function MyError(msg, customArg) {
var e = new Error(msg);
e.name = "MyError";
e.custom = customArg;
return e;
}
This is what I do. Instead of switching on "instanceof", switch on "name" instead.
You can create an Error object by calling the Error constructor.
An error object can have a message and a name. When catching you can check for a specific name or you can create a custom Error Type by inheriting the Error prototype. This allows the use of instanceof to differ between different Error types.
// Create a new object, that prototypally inherits from the Error constructor.
function MyError(message) {
this.message = message || "Default Message";
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
try {
throw new MyError();
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "Default Message"
}
try {
throw new MyError("custom message");
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "custom message"
}
Example taken from: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error

Getting stack trace for an error when running code from the console in Chrome

I am calling a function from the console but when it throws an exception I do not receive a stack trace like I would if the code was executed normally.
Is there a way I can modify my command (perhaps with try/catch) to have it provide me this information?
to clarify:
page.js:
function otherStuff() { return ['a','b',undefined,'c'];
function doStuff() {
var x = otherStuff();
var z = parseInt(x[2]); // this will throw an error
}
console, after loading a html page that links page.js
> otherStuff();
I get no line number from the Error that is returned to me. When running it from the page (instead of the console) i would receive a line number and a stack trace.
Although verbose, this will print the stack trace of an interactive error in the Chrome JS console:
try {
throw new Error();
} catch (e) {
console.error(e.stack);
}
Unfortunately this won't work if a non-Error object is thrown.
You have an error in your code.
You are missing a closing brace:
function otherStuff() { return ['a','b',undefined,'c']; //} where am i?
function doStuff() {
var x = otherStuff();
var z = parseInt(x[2]); // this will throw an error
}
Side point:
parseInt(undefined) does not throw an error. case in point: http://jsfiddle.net/maniator/Zequj/2/

How to create, design and throw built-in Error objects

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.

Categories

Resources