1

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...

Amit
  • 45,440
  • 9
  • 78
  • 110
Patrick Bounaix
  • 125
  • 2
  • 6
  • `err: null res: true called: 1 err: Error: throw Error from foo! res: null called: 2` <-- catch was called once. Why do you think it's called twice? The callback *WAS* called twice, but that's what you wanted, no? – Amit Aug 31 '15 at 20:21
  • No, that's exactly what I want to avoid. The callback should be called once, from foo. In the first case, it gets called once when it runs successfully, and then again when an error is encountered because the promise catch block still has control. – Patrick Bounaix Aug 31 '15 at 20:23
  • 1
    Have a look at [When is .then(success, fail) considered an antipattern for promises?](http://stackoverflow.com/q/24662289/1048572) (then accept Amit's answer) – Bergi Aug 31 '15 at 21:29

1 Answers1

1

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);
 });
}
Amit
  • 45,440
  • 9
  • 78
  • 110