1

I have a function doSomething() that returns a promise chain, utilizing the Q framework. The contents are similar to something like:

loadDataSet : function (params) {

    return Q.fcall(function() {
        //Do Something
    })
    .then(function(){
       //Do Something Else
       throw New Error('unexpected error');
    });
}

The calling code goes something like:

var promise = loadDataSet(args);

I want to figure out whether that error was thrown. Notice, in the loadDataSet function implementation, I did not utilize the .done() function.

So far, I have code that looks like this and have been unsuccessful in capturing the error and handling it appropriately (here, the code is modified, slightly from above):

try {
    loadDataSet(args)
    .catch(function(error) {
       return error
    })
    .done();
}....

The goal is to handle the error from a try-catch block. What am I missing?

2 Answers2

2

Well, this is going to a be a bummer.

You can't

While a lot of promise libraries let you do this and will report unhandled rejections for you - in Q you have no methods to automatically detect these failures.

You have to Use .done or change a promise library. Heck, even native promises are going to be able to do this in a few days.

Q specific solution:

In Q your only realistic option is to use .done, unlike then done is not throw safe and you can throw exceptions from there and they won't be suppressed - this requires you to remember to always terminate chains with done but it works:

myObj.loadDataSet(handleSuccess, handleError).done(); // will throw on rejection

Personally until Q fixes this and other issues I cannot recommend using it to anyone.

Modern libraries and native promises

I've written a specification based on work by Domenic and Petka for promise libraries to be able to report errors globally and hook to them. Several libraries already implement this including bluebird and when. Domenic is working on a parallel specification for web browsers.

Currently supported or are going to be supported in the next few weeks are: bluebird, when, es6-promise, rsvp and native promises in io.

// log any unhandled promise rejections
process.on('unhandledRejection', function(reason, p){
    console.log("Possibly Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging here
});

As for browsers, something like:

window.addEventListener("unhandledrejection", function(e) {
    var reason = e.detail.reason;
    var promise = e.detail.promise;
    console.log("Unhandled rejection", promise, reason);
});

This protocol is less supported but there are plans to include it in native promises. Currently Firefox native promises will report unhandled rejections and Chrome's will attempt too - but there are no browser hooks for it yet (it's coming though).

Note that teams are working on very interesting tooling. After a discussion with Paul Irish I'm convinced great things are going to come our way in terms of tooling for debugging promises in browsers that will make native promises almost as debuggable as bluebird promises (which is awesome!).

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Good answer, but it somehow doesn't match the question about handling errors in `try catch` blocks… – Bergi Feb 10 '15 at 13:29
  • @Bergi yeah we both know that's impossible :) OP asks "I want to figure out whether that error was thrown. Notice, in the loadDataSet function implementation, I did not utilize the .done() function." - his try/catch example was just to illustrate an attempt to solve the problem for a particular case. I don't mind addressing this in particular in the answer if you think it's important. – Benjamin Gruenbaum Feb 10 '15 at 13:51
-2

You can't throw exceptions inside then as no one will able to catch it. Instead, create a new Q.deferand call reject on it whenever there's an error

loadDataSet : function (params) {
    var deferred = Q.defer()
    Q.fcall(function() {
        //Do Something
    }).then(function(){
       //Do Something Else
       deferred.reject('error message')
    }, deferred.reject)
    return deferred.promise
}

then use it like this

loadDataSet().then(function (data) {
    //ok, got data
}).catch(function (err) {
    //error!
})
Cranky
  • 42
  • 1
  • 3
  • 3
    Wrong. You can throw exceptions in `then()` callbacks, and they will result in failed promises. There is never a reason to use `Q.defer()` if you already have a promise. Read http://blog.slaks.net/2015-01-05/introducing-promises/ – SLaks Feb 09 '15 at 19:14