figure 2: generating stacks.
function myStack() {
var caller, depth;
var stack = new Array();
for (caller = arguments.callee, depth = 0;
caller && depth < 12;
caller = caller.caller, depth++) {
var args = new Array();
for (var i = 0; i < caller.arguments.length; i++)
args.push( caller.arguments[i]);
stack.push({
caller: caller,
args: args
});
} this.stack = stack;
}
figure 3: Wrapping asynchronous requests.
(a)
function dosomething(a, b)
{
service.dosomething(a + b, function (ret, err) {
if (err)
throw (err);
process(ret);
});
}
(b)
function dispatch(func, args, callback) {
var stack = new myStack(); dodispatch(func, args, function (ret, err) {
try {
callback(ret, err);
} catch (e) { e.linkedStack = stack; myHandleException(e);
}
});
}
expensive to assemble, we can achieve this by recording the stack trace before dispatching every asynchronous call and then chaining it to any caught exception. The global exception handler will print all members of the exception, displaying both stack traces in the process. Our core dispatch routine would look something like Figure 3b. This allows transparent handling of server-side failures using the same exception handler. If an asynchronous closure generates an unanticipated exception, we can include the context in which the original XMLHTTPRequest was made.
By carefully following these design principles, we can construct an environment that dramatically improves our ability to debug issues by enabling users to provide developers with richer information that will allow for further analysis. Unfortunately, this environment is required to overcome the inad-equacies of current JavaScript runtime environments. Without a single point to handle all uncaught exceptions, we are forced to wrap all callbacks in a try/ catch block; and without reliable stack traces, we are forced to generate our own debugging infrastructure. It seems clear that a browser that implements these two features would soon become the preferred development environment for AJAX applications. Until that happens, careful design of the AJAX environment can still yield dramatic improvements in debuggability and serviceability for users of an application.
lar object. To get a function’s name, we simply need to search the members of the window object, all of their children, and all children of their prototype objects. If we find a match, then we can construct the name using this lineage.
With the function name and the arguments, we can display a reasonable facsimile of a stack trace, even on browsers without native support for stack traces. One caveat, however, is that getting function names doesn’t work with Internet Explorer 7. For reasons that are not well understood, global functions are not included when iterating over members of the window object.
Careful construction of the global exception handler allows us to handle both native browser and dynamically generated exceptions. Although having stack traces attached to our cus-
tom exceptions is useful, the true power of this mechanism is evident when dealing with asynchronous closures in a complex environment, particularly asynchronous XMLHT TPRequest objects. In a complicated AJAX application, all server activity must happen asynchronously; otherwise, the browser will hang while waiting for a response. A typical service model will look something like Figure 3a.
If an exception occurs in the process() function, then a wrapper embedded in the service implementation will catch the result and hand it off to our exception handler. But the stack trace will end at process(), when what we really want is the stack trace at the point when dosomething() was called. Because our stack traces are generated on demand and are in-
Related articles on queue.acm.org
Making the Move to AJAX (Case Study)
Jeff Norwalk http://queue.acm.org/detail.cfm?id=1515744
Debugging in an Asynchronous World
Michael Donat
http://queue.acm.org/detail.cfm?id=945134
Debugging Devices
(Kode Vicious column)
http://queue.acm.org/detail.cfm?id=1483103
Eric Schrock has been a staff engineer at sun Microsystems since 2003. after starting in the solaris kernel group—where he worked on zFs, among other things—schrock spent the past few years helping to develop the sun storage 7000 series of appliances as part of the company’s Fishworks engineering team.
The complete source code for the examples included here, as well as the latest version of the browser support table, can be found at http://blogs.sun.com/eschrock/ resource/ajax/ index.html.
© 2009 aCM 0001-0782/09/0500 $5.00
References:
http://queue.acm.org/detail.cfm?id=1515744
http://queue.acm.org/detail.cfm?id=1483103
http://blogs.sun.com/eschrock/resource/ajax/index.html
Archives