How can I avoid my Q promise catch block being called twice in a NodeJS callback-style API when running mocha unit tests?

We're using the Q promise library in our node API, but allow functions to be called via callbacks.

For example:

function foo(cb) {
 Q.fcall(function () {
    return true;
 }).then(function (result) {
    return cb(null, result);
 }).catch(function (err) {
    return cb(err, null);
 });
}

When I run my mocha unit tests, if there is an exception in the callback, it results in the the callback being called twice.

For example:

var called = 0;
foo(function (err, res) {
  called++;
  console.log('err: ' + err);
  console.log('res: ' + res);
  console.log('called: ' + called);

  throw Error(throw Error from foo!');
});

This gives the following result:

err: null res: true called: 1 err: Error: throw Error from foo! res: null called: 2

One approach we found was to do the following:

function foo2(cb) {
 var _result, _err;
 Q.fcall(function () {
     return true;
  }).then(function (result) {
     _result = result;
  }).catch(function (err) {
     _err = err;
  }).finally(function () {
     _.defer(cb, _err, _result);
  });
}

The idea was to have one place where the callback would be called and try to prevent developer errors by enforcing the defer to clear the call stack.

With this approach, our callback is called once, and any errors (or in our case, asserts) get handled directly by the caller.

Is there a better technique we can use? I feel like this is a pretty common scenario, so I'd imagine there exists a cleaner solution...


ANSWERS:


Modify your foo function to handle both the fulfillment and the rejection in the same then call using 2 separate handlers:

function foo(cb) {
 Q.fcall(function () {
    return true;
 }).then(function (result) {
    return cb(null, result);
 }, function (err) {
    return cb(err, null);
 });
}


 MORE:


 ? make function wait for a promise to return nodejs
 ? return the value via promise in nodejs using q library
 ? kdb/q, reserved word as column name
 ? kdb/q, reserved word as column name
 ? kdb/q, reserved word as column name
 ? KDB string concatenation with symbol list for dynamic query
 ? How to apply a function to an entire column?
 ? How to save what I have selected into a new table in KDB
 ? Loading a large binary file with kdb+ (Q)
 ? kdb update multiple columns corresponding to multiple where clauses