uncaught exception in when() body disappears in void

30 views
Skip to first unread message

Geert-Jan Brits

unread,
Mar 6, 2013, 8:54:55 AM3/6/13
to cuj...@googlegroups.com

I'd like to get a feel of general excepted exception-handling while using When().

I've got this init-code which creates the app and returns a promise when the app is done initializing.

The remainder of my code (the stuff depending on the app being initialized) goes in the body of the when, like so:

var app = new App().create(config);
when(app.createPromise,function(){
//everything runs here
}, function(err){
console.log(err)
});

While developing I'd like to throw the occasional exception around, which is caught in the controllers, the usual stuff. However, as everybody knows, sometimes you get an uncaught exception which bubbles up (hopefully only during dev), which normally is outputted on the console.

However, now I've got everything wrapped in a when() the thrown exception just disappears in void. It' s also not caught by the attached exception handler, which is pretty logical since I'm not doing a promise.reject().

What's the best way to approach this? I mean in this particular example, I'm just solving it without promises (go the oldfashioned callback route) , but I'd like to know the general way.

Or in short: 
How to have a catch-all for exceptions when promises are involved ?


Thanks, Geert-Jan

Brian Cavalier

unread,
Mar 6, 2013, 11:04:24 AM3/6/13
to cuj...@googlegroups.com
Hey Geert-Jan,

One key to working with promises is understanding how errors work.  First a bit of background.  Promise error handling is modeled after exceptions (although not *exactly*), but since promises are asynchronous, and represent code that executes across many disjoint stack frames, normal synchronous throw/try/catch/finally simply cannot work.

Ok, let's look at a slightly modified version of your example and dissect what it means.

var app = new App().create(config);
var resultPromise = when(app.createPromise, doSomething, handleError);

In this case, we have 2 promises: app.createPromise and resultPromise.  The call to when() on line 2 says: "if app.createPromise fulfills successfully, call doSomething.  If app.createPromise rejects, call handleError".  This is an important guarantee of promises: either doSomething will be called or handleError will be called, but never both.

The result/outcome of calling doSomething or handleError is captured in resultPromise.  This is important, and is what makes promises so incredibly useful: successes and failures propagate out of functions, up the call stack, just like non-promise return values and thrown exceptions, but are captured in a promise (since callstacks will be disjoint).

On to your specific question: What happens to an error that occurs in doSomething()?

It is captured in resultPromise.

You can observe the error by observing resultPromise.  Let's refactor your example slightly to see how that works:

var app = new App().create(config);
var resultPromise = when(app.createPromise, doSomething);

// resultPromise represents either the successful result of doSomething
// OR ONE OF TWO POSSIBLE FAILURES:
// 1. The failure generated if app.createPromise rejected, or
// 2. The failure generated if doSomething throws an exception or returns a rejected promise
resultPromise
.otherwise(handleError);

(NOTE: .otherwise(handleError) is equivalent to .then(null, handleError), but looks nicer and is easier to read, imho)

This becomes very powerful when you start thinking about returning promises from functions.  Consider this:

function init() {
 
return when(new App().create(config).createPromise, doSomething);
}

function otherCode() {
  init
()
 
.otherwise(
   
// As above, this will be called for one of two possible errors
   
// 1. The failure generated if app.createPromise rejected, or
   
// 2. The failure generated if doSomething throws an exception or returns a rejected promise
      handleError
 
);
}

Now imagine if we weren't using promises, and both init() and App.create were synchronous.  We'd probably write something like this:

function init() {
 
return doSomething(new App().create(config).createdObject);
}

function otherCode() {
 
try {
    init
();
 
} catch(e) {
   
// As with promises, e again represents one of two possible failures!
   
// it is either an exception thrown by new App().create() or an exception
   
// thrown by doSomething.
    handleError
(e);
 
}
}
 
Notice how similar the synchronous and promise-based async version are, even in their error propagation characteristics!  Even though the promise-based version will execute asynchronous, the ultimate result will be the same.

I've written a couple blog posts on this very subject.  They provide even more explanation and discussion of how promise error handling is similar to exceptions.  Hopefully, they'll help as well.
I hope that helps, but please feel free to continue the discussion!

Geert-Jan Brits

unread,
Mar 6, 2013, 4:45:54 PM3/6/13
to cuj...@googlegroups.com
Extremely helpful Brian! Especially how to propogate out of the stack.
 
Thanks again

Brian Cavalier

unread,
Mar 6, 2013, 7:17:53 PM3/6/13
to cuj...@googlegroups.com
Great, glad that helped.

b

thanpolas

unread,
Mar 12, 2013, 4:50:16 PM3/12/13
to cuj...@googlegroups.com
Awesome writeout Brian!

It helped me realize why i was having issues.

My latest issues are now with test suites. 

If a test is asynchronous, it will fail to detect the fail and instead timeout when the callback is within a context of a promise.

e.g.

it('should be true', function(done) {

function asyncCB( res ) {
  // this will fail, but as exception is catched by when, it does not bubble to the testing lib.
  expect( false ).to.be.true;
  done();
}

promise.then(asyncCB);

});

Brian Cavalier

unread,
Mar 13, 2013, 9:01:01 AM3/13/13
to cuj...@googlegroups.com
Hey Thanasis, I know we discussed this briefly in IRC yesterday, but I figured I would capture our discussion so hopefully others can benefit from it as well.

Yeah, you're right in that many test runners unfortunately cannot handle this situation.  We use BusterJS, and it will handle it.  It also allows returning promises from unit tests, which is really awesome.  In fact, BusterJS uses when.js internally for its promises :)  Check out their docs on async testing for more info.

There are some extensions to other popular assertion libs that allow them to work with promises.  Domenic Denicola has created a couple that I'm aware of:


Hope that helps!
Reply all
Reply to author
Forward
0 new messages