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.
Related
I'm a JS game dev who's been trying to combat tampermonkey scripts for a while now.
I came up with a solution for people hooking into WebSockets where I'd cause the WebSocket to throw an error new WebSocket(0); (0 throws an error due to it being a number)
let output;
try {
output = new target(...args);
} catch(e) {
let source = e.stack.substring(e.stack.indexOf("("), 1 + e.stack.indexOf(")"));
e.stack = e.stack.replace(source, "nothing to see here");
throw e;
}
this code made the error's stack have all the information I was looking for replaced!
I've been looking at Object.defineProperty, and I was wondering how I could stop an error's stack from being modified before I have access to that specific error. And if anyone has any other ways I could stop a script from being loaded or run, I'd love to hear them!
One thing you could do is Object.freeze the error before throwing it. This would prevent people from altering the object's contents.
So for example:
try {
new WebSocket(0);
} catch (wsErr) {
throw Object.freeze(wsErr);
}
The code catching your error and trying to alter it would fail to be able to alter it. This should work as it will cause the code that was altering the error to throw with the following:
Cannot assign to read only property 'stack' of object ''
The other thing you'll have to consider is that in your code where you're catching the error, you will not be able to alter its contents either. Typically with errors, that's not a huge deal though. Tampering with errors is one of the only reasons I can think of for modifying the error.
I cannot find anything on this so I assume I am either searching for the wrong thing or this is not a common thing to do.
I am writing a basic library that abstracts a few underlying libraries. Each underlying library throws its own pretty ambiguous error messages that mean very little to the end user. Some are also extended with different properties.
Currently I catch these errors and throw a new error which makes more sense to the end user and are consistent in structure. However, this loses the stack trace from the original error and I want to preserve this as it contains some information valuable to the end user.
At first I thought a custom Error class was the way to to keep type checking, so I started with something like
class WrappedError extends Error {
readonly stack;
constructor(message: string, error?: Error) {
super(message);
if (error?.stack) {
this.stack = error.stack
}
}
}
but quickly realised that even if this works (EDIT: just checked, the trace does not continue) then the error.stack would only contain the trace from the original throw to my catch and creation of a new WrappedError - everything after would be missing.
This is making me think that my last realistic option is to mutate the original error and rethrow it
catch (error) {
error.message = `Error reason due to such and such, but also; ${error.message}`
throw error;
}
I'm not a massive fan of mutation. And also at this point I quite like the idea of having a single custom error type from my class.
Can stacks be preserved and continued? Or what is the best way to achieve this? Or is there a convention used in JS?
It seems a native solution was added somewhat recently, so a clean solution would be
try {
...
} catch (error: unknown) {
throw new Error('Error reason due to such and such', { cause: error });
}
JavaScript, when throw-ing a built-in error as such:
throw new Error("Something was wrong");
displays the text nicely - you can't tell you threw an object
However, when creating a custom error by subclassing the Error object (or other error object for that matter), the thrown error is not displayed the same way in the console.
So, by using this code:
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
}
ImproperlyConfigured.prototype = new Error();
The following is the output
I don't like the fact that the object properties (name and message) are shown. In fact, I don't like that I don't understand why the properties are shown.
I've googled a bit and I got the feeling that by implementing a toString method will help but, by using this method, the fact that the name of the error is no longer in red puzzles me even more.
Code
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
this.toString = function(){
return this.message;
}
}
ImproperlyConfigured.prototype = new Error();
Output:
What I would like to achieve is a standard looking error, by the use of custom error and, of course, by not using the console.error method manually in a try...catch block.
Is this possible?
As Pointy correctly pointed out (pun intended), the issue here is not with JavaScript, but rather with the environment JavaScript is running (in this case, Google Chrome).
In another environment (like Chromium, Firefox, NodeJS, etc.) the behavior will likely be different, using the same code, depending on how those JavaScript hosts are handling these cases.
I noticed that qUnit doesn't give any notice when an exception happens in a later part of the test. For example, running this in a test():
stop();
function myfun(ed) {
console.log('resumed');
start(); //Resume qunit
ok(1,'entered qunit again');
ok(ed.getContent()== 'expected content') // < causes exception, no getContent() yet.
}
R.tinymce.onAddEditor.add(myfun)
in an inner iframe on the page will cause an exception (TypeError: ed.getContent is not a function),
but nothing in Qunit status area tells this. I see 0 failures.
(R being the inner iframe, using technique here: http://www.mattevanoff.com/2011/01/unit-testing-jquery-w-qunit/) Would I be correct in assuming this isn't the best way to go for testing sequences of UI interaction that cause certain results? Is it always better to use something like selenium, even for some mostly-javascript oriented frontend web-app tests?
As a side note, the Firefox console shows the console.log below the exception here, even though it happened first... why?
If you look into qUnit source code, there are two mechanisms handling exceptions. One is controlled by config.notrycatch setting and will wrap test setup, execution and teardown in try..catch blocks. This approach won't help much with exceptions thrown by asynchronous tests however, qUnit isn't the caller there. This is why there is an additional window.onerror handler controlled by Test.ignoreGlobalErrors setting. Both settings are false by default so that both kinds of exceptions are caught. In fact, the following code (essentially same as yours but without TinyMCE-specific parts) produces the expected results for me:
test("foo", function()
{
stop();
function myfun(ed)
{
start();
ok(1, 'entered qunit again');
throw "bar";
}
setTimeout(myfun, 1000);
});
I first see a passed tests with the message "entered qunit again" and then a failed one with the message: "uncaught exception: bar." As to why this doesn't work for you, I can see the following options:
Your qUnit copy is more than two years old, before qUnit issue 134 was fixed and a global exception handler added.
Your code is changing Test.ignoreGlobalErrors setting (unlikely).
There is an existing window.onerror handler that returns true and thus tells qUnit that the error has been handled. I checked whether TinyMCE adds one by default but it doesn't look like it does.
TinyMCE catches errors in event handlers when calling them. This is the logical thing to do when dealing with multiple callbacks, the usual approach is something like this:
for (var i = 0; i < callbacks.length; i++)
{
try
{
callbacks[i]();
}
catch (e)
{
console.error(e);
}
}
By redirecting all exceptions to console.error this makes sure that exceptions are still reported while all callbacks will be called even if one of them throws an exception. However, since the exception is handled jQuery can no longer catch it. Again, I checked whether TinyMCE implements this pattern - it doesn't look like it.
Update: Turns out there is a fifth option that I didn't think of: the exception is fired inside a frame and qUnit didn't set up its global error handler there (already because tracking frame creation is non-trivial, a new frame can be created any time). This should be easily fixed by adding the following code to the frame:
window.onerror = function()
{
if (parent.onerror)
{
// Forward the call to the parent frame
return parent.onerror.apply(parent, arguments);
}
else
return false;
}
Concerning your side-note: the console object doesn't guarantee you any specific order in which messages appear. In fact, the code console.log("foo");throw "bar"; also shows the exception first, followed by the log message. This indicates that log messages are queued and handled delayed, probably for performance reasons. But you would need to look into the implementation of the console object in Firefox to be certain - this is an implementation detail.
i'm tryin to identify the error i get in a javascrip function in my webpage, so i added
function guardarMisDatos() throws Exception {
try{
...
} catch (Exception e){
alert("error: ", e);
}
but when i open the page, the chrome web console gives me error at
function guardarMisDatos() throws Exception {
and the error type is "Uncaught syntaxerror: unexpected identifier"
where is the error? is it a correct way to check way the function is not fired on the first click?
It is JavaScript not Java. Lose the throws Exception!
Your code looks a lot like Java, not javaScript. The syntax for try/catch in javaScript goes like this:
try {
// do stuff
} catch (e) {
// something bad happened
}
Notice there is no throws and no type on e (since javascript is loosely typed)
Remove "throws Exception" and the catch reference to "Exception". To know what kind of exception it is, look at the e.name property, it'll be one of six things:
EvalError - An error in the eval() function has occurred.
RangeError - ut of range number value has occurred.
ReferenceError - An illegal reference has occurred.
SyntaxError - A syntax error within code inside the eval() function has occurred. All other syntax errors are not caught by try/catch/finally, and will trigger the default browser error message associated with the error. To catch actual syntax errors, you may use the onerror event.
TypeError - An error in the expected variable type has occurred.
URIError - An error when encoding or decoding the URI has occurred (ie: when calling encodeURI()).
These aren't constants, they're the actual string, as in if (e.name.toString()=="TypeError") There are a lot of other good things on the error object too, read more at http://www.javascriptkit.com/javatutors/trycatch2.shtml
Remove the throws Exception from your function definition. You do not need this in JavaScript. Besides that, why would your function ever throw an exception - you already catch it!