promisees6-promisewhen-js

when - Unexpected Promise chain with intermediate errors


I'm wondering if the following is a normal behavior?

Code

var when = require('when'); // I'm using when@3.7.4 node_modules/when

console.log("INIT");

when.promise(function(resolve, reject) {
  return when.reject("false")
  .then(function(ok) {
      console.log("Step 1 - in then, ok = %s", ok);
      return 'ok1';
  }, function(err) {
      console.log("Step 1.1 - in catch, err = %s", err);
      return reject(err);
  }).then(function(ok) {
      console.log("Step 2 - in then, ok2 = %s", ok);
      return resolve("done");
  }).catch(function(err) {
      console.log("Step 3 - in catch, err = %s", err);
      return reject(err);
  });
}).then(function(mainok) {
    console.log("Step 9 - in main then, mainok = %s", mainok);
}).catch(function(err) {
    console.log("Step 9 - in main catch, err = %s", err);
});

Here is the output I received when running it

INIT
Step 1.1 - in catch, err = false
Step 2 - in then, ok2 = undefined
Step 9 - in main catch, err = false

Reading the API I was expecting that step 1.1 would be called, then step 9 but not step 2.

Is that a bug or did I misread the API?

Thanks for your hints!


Solution

  • Yes, this is the expected behavior. Here are the steps I see:

    1. You create an outer promise that is waiting for resolve() or reject() to be called on it.
    2. You then create an inner promise that starts out rejected.
    3. It then goes to the second handler for the first .then() handler (e.g. the reject handler) and you see "Step 1.1" output.
    4. That 1.1 handler then rejects the outer promise, but the processing continues on the inner promise.
    5. Because you had a .then() handler for the rejection of the inner promise and you didn't throw or return a rejected promise from that handler, the state of the inner promise switches to resolved. If you "handle" a rejected promise and don't throw or return a rejected promise, then the state switches to fulfilled.
    6. Thus, the next .then() handler that is called is "Step 2" because the promise is now fulfilled, not rejected.
    7. That resolve handler then calls resolve("done"), but the outer promise has already been rejected to trying to resolve() it now does nothing. It's state has already been set and cannot be changed.
    8. Since the inner promise is now fulfilled, it skips the "Step 3" .catch() handler.
    9. The outer promise was earlier rejected so it skips the "Step 9" .then() fulfilled handler and it goes to the last .catch() handler "Step 9 - in main catch".

    Keep in mind that all promises are resolved or rejected asynchronously. Calling reject() on the outer promise does not immediately run the .catch() handler for the outer promise. It schedules it to run in the future.