How do I capture and resume global errors in javascript? - javascript

I am writing a testing suite for javascript and need a way to capture any JS errors and continue processing the rest of the page.
I can't use window.onerror since return true stops the browser from proceeding. I tried using a try {} catch block but the function is run in a window setTimeout() for various reasons and that seems to mess up the try catch block.
For example, I do something like this:
function test(msg, fn) {
$('#output').text(msg);
try {
fn.apply(this);
} catch(e) {
document.write('error'+e);
};
}
and then call it like this
test('trying this', function() { setTimeout('afunction()',100); });
but if afunction() fails the error is not being caught.
Does anyone have any idea on a solution or global error handler that allows me to resume?

There is window.onerror, which you can attach a handler function to. If that function returns true, the error is suppressed: http://jsfiddle.net/pimvdb/qAv9J/2/.
window.onerror = function(e) {
console.log(e);
return true;
};

Related

How to throw an error inside of a call back, and catch it outside

Intro
Inside of this jQuery(document).ready() function, inside the anonymous function, I am throwing an error which gets caught inside of the second catch block. Then I am throwing an error inside of that catch block AND trying to catch it outside in the first try catch block.
Questions.
Is this possible? BROWSER CONSOLE says "UNCAUGHT."
If so, how is it possible?
Any other info would be greatly appreciated - Trying to wrap my head around this.
try {
// bind to ready
jQuery(document).ready(function () {
try {
throw Error("Can this be caught outside of this jquery callback?");
_this.initialize();
} catch(e) {
console.log(e.message + ' Caught inside callback');
throw Error(e.message + ' Not caught outside');
}
});
} catch(e) {
//NEVER gets executed.
console.log(e.message);
}
Event handlers for $(document).ready are executed asynchronously to the code that registers them - if the page is still loading, when the registration occurs. Your function() { ... throw ... } executes outside the outer try/catch block. What you are asking is not possible.
Explanation of behaviour observed by #RickHitchcock in https://jsfiddle.net/a3vttpk6/ (see first comment to the question)
If in Rick's fiddle you click on Javascript settings button, you will see that "LOAD TYPE" setting is "onLoad". This means that the whole js code in this window is wrapped in window.onload = function() { ... }. The consequence of this is - the immediate execution of $(document).ready handlers, because the page has already loaded. If you choose "no wrap" setting - you will see the behaviour described in the question.
let promise = new Promise((resolve, reject) => {
jQuery(document).ready(() => {
resolve('documentReady')
});
})
promise.then((response) => {
//run code here like you would in the ready callback
throw Error("Can this be caught outside?");
}).catch(error => console.log(error, 'Caught you!'))
Leveraging promises, we can run a function after the document ready event fires. If there's an error, we can throw an error that the Promise catches. Should work just fine. While not technically catching an error from the ready callback, this has the same functionality.
To divorce it entirely, just assign a variable to promise.then() and reference it with .catch later.

Testing URL schemes with try/catch

I'm trying to test URL schemes via javascript try/catch blocks but my catch blocks are not being executed. Safari just skips them entirely and I don't understand why?!
function open_twitter() {
if (!IS_IOS){ //made unequal IOS so that I could test it on my Macbook
try {
window.location = "twitter://"; //fails and should go to the catch block
} catch (e) { //skipped
try { //skipped
window.location = TWITTERRIFIC_URL_SCHEME; //skipped
} catch (e) { //skipped
null; //skipped
} //skipped
} //skipped
} else {
window.open(TWITTER_URL, "_blank");
}
}
Does anyone have an idea why this is happening?
I want the code to test the first URL scheme. If that isn't successful than it should (in theory) go to the catch block and run the code inside which is in my case a try block again with the next URL scheme to test and so on and so forth.
But this doesn't happen?! I'm really confused...
Try catch doesn't work exactly like what you wrote, You'll need to provide your catch with an exception, Else it doesn't know something bad happened.
See these docs : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
try/catch should be used as a simple test with your custom exception. For example,
try
{
if(false==false)
{
throw "myException"; // generates an exception
}
catch (e) {
// statements to handle any exceptions
logMyErrors(e); // pass exception object to error handler
}
As you can see, The try should test for something and return an exception if some condition is met.

Javascript error stops code execution

Whenever an error occurs inside an event handler, it stops code execution entirely so the second event callback isn't called.
For example:
$(function() {
window.thisDoesntExist();
}
$(function() {
//Do something unharmful and unrelated to the first event
}
You can easily solve the problem in this (simplified) example by adding try/catch in both anonymous functions, but in reality these functions often add several other event handlers which in turn would require try/catch. I end up with very repetitive code stuffed with try/catch blocks.
My projects has a modular design where each feature is in a different JS (and gets concatenated during a build process). I'm looking for a more generic way to handle errors inside each feature so that the error doesn't stop code execution of the other features.
I already tried following solutions:
- window.onerror (even if you return true in this function, code execution is stopped)
- $(window).error() => deprecated and code execution stops
You could create a helper function to prevent duplication of the same boilerplate code.
function tryFunction(f, onerror) {
try {
if (typeof f == 'function') {
return f();
}
} catch (e) {
return onerror(e);
}
}
$(function() {
var result = tryFunction(window.thisDoesNotExist, function (error) {
alert('Whoops: ' + error);
});
});
I created a little demonstration. It's slightly different but the same idea.
You can simply call if (typeof myFunction == 'function') before calling myFunction()
And optionally wrap it in a generic function like said by Bart to have the choice to log an error in the console if your function does not exists.
If your webapp is huge with many interaction and JS, too many try catch could alter the global performance of your application.
I would try something like this with a wrapper which will handle the try catch for you (see below, or this jsfiddle : http://jsfiddle.net/TVfCj/2/)
From the way I'm (not, and not really) handling the this and the arguments, I guess it's obvious I'm beginning with js. But I hope you get the idea, and it is correct/useful.
var wrapper = {
wrap: function wrap(f) {
return function (args) {
try {
f.apply(null, args);
} catch (ex){
console.log(f.name+" crashed with args "+args);
};
};
}
};
var f1 = function f1Crashes(arg) {
return window.thisDoesntExist();
};
var f2 = function f2Crashes(arg) {
return window.thisDoesntExist();
};
var f3 = function f3MustNotCrash(arg) {
wrapper.wrap(f1)(arg);
wrapper.wrap(f2)(arg);
}
f3('myarg');
The try-catch pattern you mention attempting in your question is the correct way - you want try-catch blocks, not a way to silently truck through module errors (in general always be extremely careful handling exceptions globally and continuing, that way lies data corruption bugs you only find 6 months later).
Your real problem is this:
... in reality these functions often add several other event handlers which in turn would require try/catch. I end up with very repetitive code stuffed with try/catch blocks.
The fix for that is Promise. This is a new structure, native in most browsers but easily shimmed in the slow ones (ahem, IE), that gives you a standard way of managing both the event callback and the exception from the event.
With a Promise your code makes a promise to always do something: either resolve/succeed or reject/fail.
function moduleA() {
return new Promise(function (resolve, reject)
{
try{
var result = window.thisDoesntExist();
resolve(resolve); // Success!
}
catch(err){
reject(err); // Fail!
}
});
}
This is better because rather than nest try-catch blocks in each callback you can instead chain promises:
moduleA().
then(moduleB).
then(moduleC).
catch(errorHandler); // Catch any error from A, B, or C
You can also handle an error and continue:
moduleA().
catch(continuableErrorHandler). // Catch any error from A
then(moduleB).
then(moduleC).
catch(errorHandler); // Catch any error from B or C
You'll still need lots of try-catch blocks in callbacks, but anything that has been wrapped in a Promise can be treated in the same modular way.
Coming next in JS is async and await, but you can use them now with a transpiler. These use promises to make code that is much easier to read, and most importantly (for you) have a single try-catch at the top that gathers exceptions from the entire Promise chain.
This answer is already too long, but I've blogged about that in more detail.
TL;DR: If your problem is "very repetitive [event callback] code stuffed with try/catch blocks" try using Promise instead.
I found a solution. When using setTimeout, the code is executed in a seperate thread, therefor it won't break any other parts of the webpage.
$(function() {
setTimeout(function() {
window.thisDoesntExist();
}, 0);
});
$(function() {
setTimeout(function() {
//Do something unharmful and unrelated to the first event
alert("This passes")
}, 0);
});
In this example, the second function is run, even when the first one throws an error.
Here's a working example: http://jsfiddle.net/mathieumaes/uaEsy/

How to roll up a JavaScript error?

In the following code, I intentionally throw an error, but in Chrome (used simply for testing purposes) it does not roll up to the catch. How can I roll up the error into the parent's scope?
try {
setTimeout(function() {
console.log("Throwing Error...");
throw({message:"Ouch!"});
}, 500);
} catch(e) {
console.log(e.message);
}
Chrome replies with:
Uncaught #<Object>
(anonymous function)
Here is the full example I'm working with; when I require "bob" it (intentionally) times out. I want to catch the requirejs error so I could use my application's error system, which is more robust, to notify the learner.
(function() {
try {
var scriptVersion = "1.0.0.1"
window.onload = function() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "//content.com/pkg/" + scriptVersion + "/require-jquery.js";
script.async = false;
script.done = false;
// OnReadyStateChange for older IE browsers
script.onload = script.onreadystatechange = function() {
if(!(this.done) && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
this.done = true;
require.config({
baseUrl: "//content.com/pkg/" + scriptVersion
});
require(["bob"]);
}
}
document.getElementsByTagName("head")[0].appendChild(script);
}
} catch(e) {
console.log(e);
}
})();
See the edit below for how to solve the actual problem with requireJS.
The problem is that the setTimeout() function runs in the parent's scope and completes without error. It schedules (with the system) a future callback event, but when that callback occurs in the future, the parent's scope of execution has finished and the callback is initiated from the system at the top level much like a new system event (e.g. a click event handler).
While the parent closure still exists because the anonymous function inside the setTimeout() can still reference those variables, the actual execution of the parent scope is done, thus the scope of the try/catch is done.
The execution context of the setTimeout() anonymous function is top level (initiated by the system) so there is no parent context that you can put a try/catch in. You can put a try/catch within the anonymous function, but throwing from there will just go back to the system which is what called the setTimeout() callback.
To have your own code catch any exceptions that occur inside the setTimeout() callback, you will need to put a try/catch inside the callback.
setTimeout(function() {
try {
console.log("Throwing Error...");
throw({message:"Ouch!"});
} catch(e) {
console.log(e.message);
}
}, 500);
If you explained what the real problem is that you're trying to solve (rather than this manufactured test case), we may be able to offer some useful options.
Edit now that you've shown what problem you're really trying to solve. The require.js library initiates every error by calling the onError method. The default implementation of the onError method is what throws the exception. You can assign your own onError handler and handle the errors in a callback rather than with exceptions. This sounds like the right way to go.
From the requirejs source:
/**
* Any errors that require explicitly generates will be passed to this
* function. Intercept/override it if you want custom error handling.
* #param {Error} err the error object.
*/
req.onError = function (err) {
throw err;
};
Your throw happens some time after the catch block, when the browser calls the setTimeout callback.
(catch uses logical scoping, not lexical scoping)
The previous answerer explained it correctly.
Another way of thinking about it is that it is not working because setTimeout completes fine and does not throw and exception when it is initially run. It then executes later when you are no longer within the try-catch block.
It will work if you put the try catch inside the setTimeout function like this:
setTimeout(function() {
try {
console.log("Throwing Error...");
throw({message:"Ouch!"});
} catch(e) {
console.log(e.message);
}
}, 500);
Let me know if you still have questions.
Use wrapper function like this.
// wrapper function
var tryable = function(closure, catchCallback) {
closure(function(callback) {
return function() {
try {
callback();
} catch(e) {
catchCallback(e);
}
};
});
};
function throwException() {
throw new Error("Hi");
}
tryable(function(catchable) {
setTimeout(catchable(throwException), 1000);
}, function(e) {
console.log("Error:)", e);
});

jQuery exception handling

Is there any way I could catch any uncaught exception in javascript? I mean, all my "dangerous" code are in try-catch blocks. But what about exceptions that I don't handle explicitly? I'm using jQuery, my main javascript file starts with :
$(document).ready(function(){})
here I bind some events to some DOM elements. I can use try-catch blocks here, but they will catch exceptions that occur during the event binding procedure, not during the event handling. But if I used try-catch blocks in every event handling functions it would be ugly.
How should I catch exceptions that don't occur in my explicit try-catch blocks? (I don't want to write a general handler function, I just want to send the problem to my server)
You could write a function that would wrap your real handlers in a try/catch
function tc(func, msg) {
msg = msg || "Handler exception";
return function(e) {
try {
return func(e);
}
catch (exc) {
$.post( /* send exception to server? */ );
throw exc; // let nature take its course
}
};
}
(Might want to get fancier with argument handling etc.) Then when you bind handlers you'd do:
$('#whatever').click(tc(function(e) {
// your handler function
}, "This is the message sent to the server when this handler fails"));
Also, if you want to make sure "this" in the handler behaves as before, you can do func.apply($(this), e) instead of just func(e).
You can use the window.onerror event handler, it's not supported in Opera though and it may not fire in certain situations (thanks #Josh).
It's not really wise to do this, however, it will make bug finding a nightmare for a start. It's generally best to make sure your code is error free in the first place :-) You certainly shouldn't need to use try... catch statements very often in JavaScript and you definitely shouldn't be using empty catch blocks.
I can use try-catch blocks here, but they will caught exceptions that occured during the event binding procedure, not during the event handling.
You can also add try/catch blocks to inner scopes:
// Outer
try {
$(document).ready(function(){})
}
catch (e) {
/* Error handling */
}
// Inner
$(document).ready(function(){
try { /* ... */ } catch (e) { /* Error handling */ }
});
How about
function func_madness() {
throw("This function is throwing exceptions, because \n" +
"it can not throw polar bears and whales.\n");
} // func_madness
window.onload = function () {
try {
func_madness();
} catch (err) {
document.write("Caught it!\n<br/>\n" + err);
} // catch
} // window.onload

Categories

Resources