Learning promises and exceptions vs. rejecting in

2019-05-26 05:46发布

问题:

I'm under the impression that an exception inside a promise will trigger the subsequent fail-handler, but I'm not seeing it happen in this code:

var Q = require('q');

function x() {
  console.log('x');
  var deferred = Q.defer();

  setTimeout(
      function() {
        console.log('resolving x');
        deferred.resolve('hi');
      },
      1000
  );

  return deferred.promise;
}

function y() {
  console.log('y');
  var deferred = Q.defer();

  setTimeout(
      function() {
        console.log('throwing in y');
        throw new Error('Oih!');
      },
      1000
  );

  return deferred.promise;
}

x().then(y).then(
    function () {
      console.log('yes');
    },
    function () {
      console.log('no');
    }
);

Am I doing something wrong, or have I misunderstood?

回答1:

You are mixing callbacks and promises + you have a case of deferred anti-pattern. Either use Q.delay or a wrapper like:

function delay(ms) {
    var d = Q.defer();
    setTimeout(d.resolve, ms);
    return d.promise;
}

Now that there is promise API for timeouts, there is no reason to use setTimeout:

function x() {
    return delay(1000).then(function () {
        console.log("hi");
    });
}

function y() {
    return delay(1000).then(function () {
        console.log('throwing in y');
        throw new Error('Oih!');
    });
}

Use:

x()
.then(y)
.then(function() {
    console.log('yes');
})
.catch(function(e) {
     console.log('no');
});


回答2:

All exceptions inside a promise chain are catched. You must either manage a failing promise as you did, or end the promise chain using done() in order to expose and throw the error (see the documentation).

However, the error you're throwing in the setTimeout is not related to the promise as the error is thrown asynchronously, see for instance this relevant question. The alternative is to reject the promise like this:

function y() {
  console.log('y');
  var deferred = Q.defer();

  setTimeout(
      function() {
        console.log('throwing in y');
        deferred.reject(new Error('Oih!'));
      },
      1000
  );

  return deferred.promise;
}