Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I debug my asynchronous, promise based code if the library is swallowing all the exceptions?

The Problem

JSFiddle: http://jsfiddle.net/missingno/Gz8Pe/2/

I have some code that looks like this:

var d = new Deferred();
d.resolve(17);
return d.then(function(){
     //do some stuff...
})
.then(function(){
    var obj = a_funtion_that_returns_null_on_IE();
    var x = obj.some_property; //BOOM!
});

The problem is that when I am on IE all I can see are 'obj' is null or not an object errors, without any reference to the corresponding line number and without the debugger halting at the offending line (like I wish it would).

This kind of issue is making the code a pain to debug and the only solutions I can think of right now (messing around with the control flow library or resorting to step-by-step debugging with the debugger or console.log) are things I would rather not have to do.

What I think is going on

In order to allow errbacks to be added after the chain is fired, then will preemptively catch any exceptions thrown by the callbacks. I think this is the reason for the IE debugger not halting on the error or showing the usual error message witht the line number in it.

The error messages without the line numbers are coming from the control-flow library: it provides a deferredOnError hook that is called whenever an exception is caught and saved for later and the default behaviour is console.error-ing the Error object:

dojo.config.deferredOnError = function(err){
    //a chance to log the exception after it is captured by "then"
    //or do other things with it
    console.error(err);
}

Sadly, I could not figure out a way to get the line number or stack trace from the error object in IE and the hook is called in a way that does not allow me to just rethrow the exception and let it bubble up to the toplevel.

What I want

I want to have a better way to debug the async code then goind along with the debugger step-by-step. In the best case a way to have the debugger halt on the exceptions (as it does on unhandled exceptions) or at least a way to get line numbers or stack traces from the Error object that was thrown.

like image 354
hugomg Avatar asked Feb 07 '12 20:02

hugomg


People also ask

How do you prevent promises swallowing errors in javascript?

Javascript. Now let us look into our problem statement with which we have to prevent promises swallowing errors. Consider the following code snippet (code example), in which a scenario is illustrated (which is based on real-time working) in which we implemented chaining of . then() method one after the another.


2 Answers

2016 solution

If you're using native ES Promises do absolutely nothing; Chrome automatically reports uncaught promise rejections in the console.

"Uncaught Promise error" in Chrome dev tools

Notice how the caught one (Second fail) doesn't show anything but the uncaught rejection appears in the console after the code is done running.

like image 94
fregante Avatar answered Oct 14 '22 03:10

fregante


What I ended up doing

I added a sequencing function to my mini library of async helper functions. It basically runs a sequence of "then" calls, except that it adds extra intermediate steps to rethrow any exceptions that end up being caught by the Deferreds. It also accepts an optional error handler to catch the exceptions.

When I call it it looks like this:

go([
    function(){
        return 17;
    },
    function(x){
        //return some stuff
    },
    function(){
         var obj = a_function_that_returns_null_on_IE();
         var x = obj.some_property; //BOOM!
    }
], function(){
    //an optional error handler
});

Another reason that I did things this way is that I have lots of code that needs to work simultaneously with either sync or async code (using Deferred.when to do the chaining). Using my custom function let me use a single, unified syntax and the errors not being captured in the async case is consistent with the sync case, where there are no Deferreds involved. I also think its OK to not capture the error, since unlike in the general case, when I am using "go" I know a-priori what code will be called, so there is no need to capture exceptions for if someone needs to catch them in the future.

Also, using a custom solution gave me the freedom to enforce some personal design preferences :)


In addition to that, I ended up reducing the amount of exceptions I generate myself throughout the code base. Managing exceptions in async code is more annoying then usual and sometimes its simpler to just fallback into handling error conditions by returning null or an error code.

Also, we made sure that any exceptions that we create ourselves are instances of the builtin Error class, intead of other objects. The reason is that the builtin Error class records the line number and stack trace of where it was generated in a kind of cross-browser way.

like image 4
hugomg Avatar answered Oct 14 '22 03:10

hugomg