In our applications, most of us hopefully follow the UX best practice of catching unexpected and/or unpreventable errors and logging them back to the server-side for monitoring.
In isolated scopes, this can be achieved by setting up a try-catch block, which also allows you to inspect the pertinent Error
object itself:
try {
throw new Error("WTF");
} catch (e) {
alert("Isolated error! Details: " + e);
}
This is very useful, especially in browsers that support the stack
property of the Error
object to improve "debugability". This property's availability is also of great benefit to @eriwen's StackTrace.js, which I find to be handy (though I'm not a big fan of the StrackTrace.js API, as @eriwen and I have discussed previously).
Setting up layers of try-catch blocks quickly becomes unreasonable in large applications. As an alternative, we can attach a listener to the window
object's error
event, which is invoked whenever an error bubbles up without being handled elsewhere:
var oldOnError = window.onerror;
window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber) {
if (oldOnError) {
return oldOnError(errorMsg, url, lineNumber, columnNumber);
}
alert("Globally unhandled error! Details: " + errorMsg);
return false;
}
The problem is that this mechanism does not allow us to inspect the pertinent Error
object at all: it only provides us with four arguments at invocation time: message (string), fileName (string), lineNumber (number), and columnNumber (number). These are OK but not very immediately useful in practice.
As such, I propose the following options as fixes:
- Add a function like
getLastError()
to the globalwindow
object that would fetch the actualError
object associated with the most recent unhandled error. I would foresee common usage looking something like the following:
var oldOnError = window.onerror;
window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber) {
if (oldOnError) {
return oldOnError(errorMsg, url, lineNumber, columnNumber);
}
var e = window.getLastError();
alert("Globally unhandled error! But we now can discover its origin. Details: " + e);
return false;
}
- Alternatively (though less preferably), we could also update the invocation arguments of
window.onerror
callbacks to include a new fifth argument that would be the relevantError
object itself:While this essentially makes the first four arguments useless, I have posed it this way for the sake of backward compatibility.var oldOnError = window.onerror; window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber, e) { if (oldOnError) { return oldOnError(errorMsg, url, lineNumber, columnNumber, e); } alert("Globally unhandled error! But we now can discover its origin. Details: " + e); return false; }
- We could also add a property to the
window
object, such aswindow.error
which would basically be a getter method to achieve the same results as the aforementionedwindow.getLastError()
proposal. - To better align with Node.js, we could also handle this via a new
uncaughtException
event, which would basically be a parallel to thewindow.onError
callback except that it would receive the actualError
object (and only theError
object) as an argument. However, it is important to note that theuncaughtException
event is typically emitted immediately before exiting the Node.js application (i.e. it is a terminal error), which is a behavior we would not want to replicate for a browser environment.
When [synchronously] handling an Error
in an isolated scope, the active Error
object should be equivalent to the result of the window.getLastError()
function invocation:
try {
throw new Error("WTF");
} catch (e) {
assert(e === window.getLastError()); // true
}
I know I can't be alone here, especially since there are logged bugs for this in many trackers. For example: Mozilla's bug #355430 and StackTrace.js's Issues #26 and #9.
So... thoughts?
@Raynos:
Not so,
this
is always equal towindow
in the globalonerror
callback. If you can show me otherwise, please do.Here's my test case: http://jsfiddle.net/6mXqv/56/embedded/result/