28

I have the following code and when it's executed, it returns both "rejected" and "success":

// javascript promise
var promise = new Promise(function(resolve, reject){
  setTimeout(function(){reject()}, 1000)
});
promise
  .catch(function(){console.log('rejected')})
  .then(function(){console.log('success')});

Could anyone explain why success is logged?

c 2
  • 1,127
  • 3
  • 13
  • 21
  • 2
    `catch` plays the same role as in `try...catch`. It catches a rejection, not just taps it. To reach the expected behaviour, you need to return a rejected promise from `catch` - or better, use a single `then` with second callback. – Estus Flask Oct 16 '16 at 15:07
  • You want `promise.then(function(){console.log('success');}, function(){console.log('rejected');})` to get either one and never both. – Bergi Oct 18 '16 at 13:16
  • See also [why .catch().catch() doesn't forward the exception](http://stackoverflow.com/questions/16371129/chained-promises-not-passing-on-rejection) – Bergi Oct 18 '16 at 13:27

5 Answers5

24

The then callback gets called because the catch callback is before it, not after. The rejection has already been handled by catch. If you change the the order (i.e. (promise.then(...).catch(...))), the then callback won't be executed.

MDN says that the .catch() method "returns a new promise resolving to the return value of the callback". Your catch callback doesn't return anything, so the promise is resolved with undefined value.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
12

Could anyone explain why success is logged?

In short: a .then following a .catch in a Promise chain will always be executed (unless it itself contains errors).

The theoretical explanation

Your code is actually just a Promise chain which is first executed synchronously setting it up to complete asynchronously afterwards. The Javascript engine will pass on any reject() or Error to the first .then down the chain with a reject callback in it. The reject callback is the second function passed to a .then:

.then(
function (){
    //handle success
},
function () {
    //handle reject() and Error
})

The use of .catch is just syntactic suger for:

.then(null, function () {
    //handle reject() or Error
})

Each of the .then's automatically returns a new Promise which can be acted upon by subsequent .then's (or .catch's which are also .then's).

Visualizing the flow of your promise chain

You can visualize the flow of your code with the following example:

var step1 = new Promise (function (resolve, reject) {

  setTimeout(reject('error in step1'), 1000);
})

var step2 = step1.then(null, function () {

  // do some error handling
  return 'done handling errors'
})

var step3 = step2.then(function () {

  // do some other stuff after error handling
  return 'done doing other stuff'
}, null)

setTimeout (function () {

console.log ('step1: ', step1);
console.log ('step2: ', step2);
console.log ('step3: ', step3);

console.log();
console.log ('Asynchronous code completed')
console.log();
}, 2000);

console.log ('step1: ', step1);
console.log ('step2: ', step2);
console.log ('step3: ', step3);

console.log();
console.log ('Synchronous code completed')
console.log();

which at runtime will result in the following output in the console:

step1:  Promise { <rejected> 'error in step1' }
step2:  Promise { <pending> }
step3:  Promise { <pending> }

Synchronous code completed

step1:  Promise { <rejected> 'error in step1' }
step2:  Promise { 'done handling errors' }
step3:  Promise { 'done doing other stuff' }

Asynchronous code completed
rabbitco
  • 2,790
  • 3
  • 16
  • 38
6

For those who had a successfully resolved promise and a chain ordered like .then > .catch, but still had both your then and catch called, it may be because your then had an error-throwing bug that you can't see unless you explicitly console the error in your catch. That's one of my pet-peeves with Promises absorbing errors even in strict mode.

const promise = new Promise(resolve => resolve())
.then(() => {
    console.log('then');
    not.defined = 'This causes the catch to fire even though the original promise resolved successfully.';
})
.catch((e) => {
    console.log('catch');
    // console.error(e);
});
Modular
  • 6,440
  • 2
  • 35
  • 38
1

For me catch() was being called after a succesful promise, and no errors in .then().

The reason was, that I listened to a value that changes with the successful promise, and ran a method.

This method was throwing a silent error, because it was being counted as part of the promise.

Timar Ivo Batis
  • 1,861
  • 17
  • 21
0

Similar to @Timar, For me the reason catch was being called is that "then" contains an exception code. so after executing "then" normally and when it reaches the exception code, it handles the exception in "catch" xD

Ali Amin
  • 667
  • 5
  • 17