Global error logging for event handlers javascript - javascript

I want to log all errors in my browser side JavaScript code. I saw a lot of discussions here and on the net about window.onerror and it is clear it does not work cross browser. So, I plan to wrap top level entry functions with try - catch. Trouble is, a lot of my code is event handlers. I have not tested it yet, but I am pretty sure that no matter where the event handler function is defined, a thrown error will fire out directly to the browser implementation that calls it, not to event function declaring code. My only choice is to declare throw, catch and error log calls in every error handler even the tiniest anonymous one. I don't like that one bit.
A possible solution:
I use one method to cross browser register events. I can modify it to do something like this:
function registerEventHandler(object, handlerRef) {
var wrapperFunction = function(evt) {
try {
handlerRef(evt);
} catch {
logError(error);
}
registerEvent(object, wrapperFunction);
}
There is one major problem with that implementation. I often keep references to event handler function in order to deregister them later. This will not work as the function registered as the handler will be the wrapper, not the original one. The answer to this is to implement a wrapper -> wrapped mapping object and use it on unregister.
The question:
I dare you JavaScript magicians to come up with more clever solution than that. Maybe it could be done by somehow augmenting the event handler function before registration? This is a far as my JavaScript knowledge goes. What about yours?

I often keep references to event
handler function in order to
deregister them later. This will not
work as the function registered as the
handler will be the wrapper, not the
original one.
Why is this a problem? once the function is wrapped in the error handling, you dont really care about the original function anymore. The wrapper keeps a reference to your original function, and the wrapper is what is registered, and the wrapper is what needs to be unregistered.
Just keep a reference to your wrapper function you generate because its the only one that matters.
Also making it it's own function will make this pattern far more reusable
var protectErrors = function(fn) {
var that = this;
return function() {
try {
fn.apply(that, arguments);
} catch(error) {
logError(error);
}
};
};
var registerEventHandler = function(object, handlerRef) {
var wrapperFunction = protectErrors(handlerRef);
registerEvent(object, wrapperFunction);
};
protectErrors(fn) will return a function that runs the original in whatever context it was called in, and forwarding any number of arguments.

Related

Javascript: Errors arent being displayed on event listeners

I have a problem that is driving me crazy. I have two event listeners that appear to be identical. The are being called with the same scope but at different times. I have been using chromes debugger tool to step through them. The first one will throw an error
TypeError: Object 1 has no method 'get'
But the second example simple stops executing
el.on("change:one", function() {
debugger;
a = 1;
a.get();
});
el.on("change:two", function() {
debugger;
a = 1;
a.get();
});
I understand that simple asking why is this happening probably needs a detailed explanation of all the libraries being used etc, so my question is:
Is it possible to not display errors thrown by javascript, and how would I be able to detect whether something is overriding the error reporting functionality
NOTE: In both examples I have determined that window.onerror is null
Backbone invokes event handlers synchronously and doesn't catch handler exceptions. So if you had code that looked like:
el.trigger('change:one'); // handler will throw exception
el.trigger('change:two'); // won't execute
The change:two event will never get triggered, resulting in your change:two handler never getting invoked.

Detecting event type in a Socket.IO handler

I am quite new to nodeJS and Socket.IO and am facing a problem.
Is there any way to know the type of event inside a Socket.IO handler? I have this kind of code:
// Fire the appropriate callback when we receive a message
// that.received is an array of callback functions
for (var event in that.received) {
socket.on(event, function(message) {
// Of course, this won't work because of "event" scope
that.received[event](message, this);
});
}
So, what I want to know is the actual value of "event" that triggered my handler.
I tried to inspect available variables with Chrome developer tools, but I wasn't able to find anything.
I need to do this because I am writing some kind of wrapper class around Socket.IO to handle multiple sockets (a long storty about fallback servers). I want it to be generic enough to pass my handlers to it.
Any ideas?
OK, stupid question.
I just need to do this:
// Fire the appropriate callback when we receive a message
for (var event in that.received) {
socket.on(event, that.received[event]);
}
I was passing "this" to my callback function, which is just stupid. My callbacks looked like this:
function myCallback(message, socket) {
// Some code...
socket.emit('ack', message.id);
// Some code...
}
But all I have to do is this:
function myCallback(message) {
// Some code...
this.emit('ack', message.id);
// Some code...
}
So I don't have the scope problem anymore.

The Downside to addEventListener Versus onClick Property

I understand the difference between an addEventListener and the onclick property and know how to use both. I am wondering if there is a draw back to always using EventListener's instead of using the onclick property. The EventListener appears to be much more powerful than just using the onclick atleast when dynamically generating the HTML from javascript.
Is there a memory/cpu drawback or am I safe to only use EventListeners?
This probably isn't the direction you were going in, but there are a few instances where you would be unable to remove an event listener.
Event handlers are completely public, and can be modified (to a certain extent) by anyone:
// You do this
myLink.onclick = function () {
alert('hello, world');
};
// Another developer who hates you because
// he thinks that you're hitting on his girlfriend
// but you're not, you're just friends, but
// he's jealous so he doesn't understand
// does this
myLink.onclick = function () {
alert('muahahahaha');
};
// Someone else could even get rid of
// the handler entirely:
myLink.onclick = null;
But there is no publically accessible list of event listeners. The only way to remove an event listener is if you still have access to the original function:
myLink.addEventListener('click', function () {
alert('hello, world');
}, false);
There is now no way to remove that event listener. You gave it an anonymous function, so even you wouldn't be able to remove it if you wanted to.

simple javascript question

I have a 2000 line jquery file, I just broke up the file into smaller ones, If I have a function in the first file, that file # 2 is referring to, it's coming up undefined.
Every file is is wrapped in a jquery ready function, What's the best way to do this?
If the function in question is declared within the scope of the ready handler, it won't be accessible to any other code, including other ready handlers.
What you need to do is define the function in the global scope:
function foo()
{
alert('foo');
}
$(document).ready(function()
{
foo();
});
P.S. A more concise way of adding a ready handler is this:
$(function()
{
foo();
});
Edit: If the contents of each of your divided ready handlers rely on the previous sections, then you can't split them up, for the reasons outlines above. What would be more sensible would be to factor out the bulk of the logic into independent functions, put these in their own files outside the ready event handler, and then call them from within the handler.
Edit: To further clarify, consider this handler:
$(function()
{
var foo = 'foo';
var bar = 'bar';
alert(foo);
alert(bar);
});
I might then split this up:
$(function()
{
var foo = 'foo';
var bar = 'bar';
});
$(function()
{
alert(foo);
alert(bar);
});
The problem with this is that foo and bar are defined in the first handler, and when they are used in the second handler, they have gone out of scope.
Any continuous flow of logic like this needs to be in the same scope (in this case, the event handler).
Function definition should not be wrapped in another function. Not unless you really want that function definition to be private. And if I understand correctly that's not your intention.
Only wrap function invocation in the jQuery ready function.
If you're worried about your functions clashing with third party function names then namespace them:
var myFunctions = {}
myFunctions.doThis = function () {}
myFunctions.doThat = function () {}
But really, you only need to worry about this if you're creating a mashup or library for others to use. On your own site YOU have control of what gets included in javascript.
Actually, for performance reasons, it may be better to keep it in one file; multiple requests actually can take up more bandwidth... but as separate files, you would need to order them in a particular order so that there is a logical sequence. Instead of having everything in a document.ready, have each script define a method, that the page will execute within its own document.ready handler, so that you can maintain that order.
Most likely the reason it's coming up undefined is because when you have separate ready calls, the scope of the code inside those calls is different.
I would reorganize my code. Any shared functions can be attached to the jQuery object directly, using $.extend. This is what we do for our application and it works well.
See this question. Hope it helps.
Everyfile shouldnt have a ready function. Only one file should have the ready function and that should be the last file.
"wrapped in a jquery ready function" is nothing else than binding stuff to the ready event that is fired when jQuery thinks the DOM is ready.
You should only bind methods that is depending on the DOM to the ready event. It doesnt matter how many binds you make, all of the methods will be executed in the binding order in the end.
Functions provide scope in JavaScript. Your code in the jquery.ready is an anonymous function, so it is unaware of the other scopes. remove the wrappings for those JavaScript functions and declare them as regular functions, a la
$(document).ready(function ()
{
functionFromFile1();
functionFromFile2();
};

Can dynamically loaded JavaScript be unloaded?

I am writing a web application that has a static outer "shell" and a dynamic content section. The dynamic content section has many updates as users navigate the system. When a new content block is loaded, it may also optionally load another JavaScript file. In the name of good housekeeping, I remove script blocks from the DOM that apply to old content blocks, since that JavaScript is no longer needed.
The problem comes next, when I realized that although I have removed the <script> element from the DOM, the JavaScript that was previously evaluated is still available for execution. That makes sense of course, but I'm worried that it may cause a memory leak if the users navigate to a lot of different sections.
The question then, is should I be worried about this situation? If so, is there a way to force the browser to cleanup stale JavaScript?
<theory>You could go with a more object-oriented approach, and build the model in a way that each block of javascript blocks come in as their own objects, with their own methods. Upon unloading it, you simply set that object to null.</theory>
(This is fairly off-the-cuff.)
Memory use is indeed an issue you need to be concerned with in the current browser state of the art, although unless we're talking about quite a lot of code, I don't know that code size is the issue (it's usually DOM size, and leftover event handlers).
You could use a pattern for your loadable modules that would make it much easier to unload them en mass -- or at least, to let the browser know it can unload them.
Consider:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function MyModule() {
function foo() {
bar();
}
function bar() {
}
}
return MyModule;
})();
That defines a closure that contains the functions foo and bar, which can call each other in the normal way. Note that code outside functions runs immediately.
Provided you don't pass out any references to what's inside the closure to anything outside it, then window.MyModule will be the only reference to that closure and its execution context. To unload it:
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
That tells the JavaScript environment you're not using that property anymore, and makes anything it references available for garbage collection. When and whether that collection happens is obviously implementation-dependent.
Note that it will be important if you hook event handlers within the module to unhook them before unloading. You could do that by returning a reference to a destructor function instead of the main closure:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function foo() {
bar();
}
function bar() {
}
function destructor() {
// Unhook event handlers here
}
return destructor;
})();
Unhooking is then:
if (window.MyModule) {
try {
window.MyModule();
}
catch (e) {
}
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
}
If you save the evaluated code in namespaces, such as:
var MYAPP = {
myFunc: function(a) { ... }
}
"Freeing" the whole thing should be as simple as setting MYPP to some random value, ala
MYAPP = 1
This does depend on there being no other means of referencing the variable, which isn't trivial
How about loading the JS files into an iframe? Then (in theory, never tested it myself) you can remove the iframe from the DOM and remove the "memory" it's using.
I think... or I hope...
If you are worried about memory leaks then you will want to make certain that there is no event handlers in the code you want removed referring to the still existing dom tree.
It may be that you need to keep a list of all event handlers your code added, and before unloading, go through and remove the event handlers.
I have never done it that way, I always worry about when I remove nodes that there is still a reference.
Here is a good article on javascript memory leaks:
http://javascript.crockford.com/memory/leak.html
JavaScript interpreters have garbage collectors. In other words, if you don't reference anything, it won't be keeping them around.
One of the reasons why it is good to use JSON with a callback function (JSONP).
example, if you HTTP response for each JS is:
callback({status: '1', resp: [resp here..]});
And if callback() does not create a reference to the JSON object passed in as an argument, it will be garbage collected after the function completes.
If you really need to make a reference, then you probably need that data around for some reason - otherwise you would/should NOT have referenced it in the first place.
The methods mentioned to namespace objects just creates a reference that will be persisted until the reference count comes to 0. In other words, you have to track every reference and delete it later, which can be hard when you have closures and references from DOM lying around. Just one reference will keep the object in memory, and some simple operations may create references without you realizing it.
Nice discussion. Clears up a lot of things. I have another worry, though.
If I bind window.MyModule.bar() to an event, what happens if the event accidentally gets triggered after window.MyModule is deleted? For me, the whole point of namespacing and separating js into dynamically loaded modules is to avoid triggering event handlers cross-module by mistake.
For example, if I do (excuse my jQuery):
$('.some-class').click(window.MyModule.bar);
What happens if I delete window.MyModule, load another module, and click on an element which accidentally has a class called some-class?

Categories