0

This may be a stupid question, but I am unable to wrap my head around this.

Consider the following piece of code:

function throwError() {
    throw Error("can't touch this.");
}

var def = q.defer();

def.promise.then(
  function() {
    console.log("no error");
  },
  function() {
    console.log("error");
  }
);

q.fcall(throwError).then(def.resolve, def.resolve).done();

In my opinion this should print error. Instead, it prints no error. The q manual states:

The reject function is a shorthand for resolving with a rejected promise.

q.fcall(throwError) should produce a rejected promise, so the deferred should be rejected, right?

Please note that this is a purely hypothetical question, I know that this would not be used in the real world.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
raffomania
  • 451
  • 4
  • 14

1 Answers1

1

The issue here seems to be around the language used, so I'm going to try to break it down a bit more to make it clearer. When you call

var rejected = q.fcall(throwError);

then rejected is indeed a rejected promise. What you are then doing is

var def = q.defer();
def.promise.then(
  function(arg1) {
    console.log("no error");
  },
  function(arg2) {
    console.log("error");
  }
);

rejected.then(
  function(){
    def.resolve();
  },
  function(err){
    def.resolve(err); // err === new Error();
  }
);

When a promise is rejected, it will run the rejection callback, triggering def.resolve(err);. Q has no knowledge that def is anything other that some random arugment, so you might as well be calling def.resolve('random');. You are resolving def with some argument. In this case, it happens to be an instance of Error. That means that your no error callback will be called, and arg1 === new Error().

So now, given the documentation quote you mentioned

The reject function is a shorthand for resolving with a rejected promise.

what are you are doing is essentially transforming the rejected promise rejected into the promise def.promise which will always be fulfilled, but where the fulfillment handler will receive an error as the first argument.

To achieve what you are expecting, you need to properly make def.promise be rejected. You can accomplish this two ways.

  1. Reject it using def.reject(err);.
  2. As your quote says, resolve it with a rejected promise, def.resolve(rejected);.
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • Actually I wanted to use `def.resolve` twice. As I stated above, resolving with a rejected promise should trigger the error handler, not the fulfillment handler. So, IMO, the second function should be called, not the first one. – raffomania Jan 04 '14 at 02:06
  • @raffomania Sorry, not following. `q.fcall(throwError)` does produce a rejected promise as you say, but since you have passed `def.resolve` as the error handler, it will resolve `def` without an error, just like if you called `def.resolve()` somewhere else. – loganfsmyth Jan 04 '14 at 02:14
  • Perhaps the confusion is around `resolving with a rejected promise.` What you are doing now is resolving with an `Error` object, not resolving with a rejected promise. – loganfsmyth Jan 04 '14 at 02:20
  • Please have a look here: [Using deferreds in q](https://github.com/kriskowal/q#using-deferreds). There you can see exactly my example. `q.fcall` should produce a rejected promise, not an error. – raffomania Jan 04 '14 at 15:32
  • @raffomania Correct, the return value of `fcall` returns a rejected promise. Then you are calling `.then`, which extracts the error associated with the rejection, and you pass that error as the first argument. I'll expand my answer. – loganfsmyth Jan 04 '14 at 18:46
  • Now I understand. It seems so clear to me, I can't believe I didn't see this before. Thank you very much for this thorough explanation! – raffomania Jan 04 '14 at 22:34