How to handle catch blocks and then of Promise

Viewed 975

I have following code

function request(status){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(status){
        resolve('Success');
      } else {
        reject('error');
      }
    }, 1000);
  }); 
}


let promise = request(false);

promise.then( response => {
  console.log('response' , response);
});

promise.catch( (err) => {
  console.log('got Error', err);
});

throws following error even I caught the reject response

got Error error (node:11252) UnhandledPromiseRejectionWarning: error (node:11252) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:11252) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

but if I remove the then block then it works fine, NO STACK TRACE ERROR ON THE CONSOLE

function request(status){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(status){
        resolve('Success');
      } else {
        reject('error');
      }
    }, 1000);
  }); 
}


let promise = request(false);

promise.catch( (err) => {
  console.log('got Error', err);
});

Output:

got Error error

I do not understand why it works in such a way?

3 Answers

The key thing here — and one of the key things about using promises — is that then and catch create new promises. So the promise that was rejected that wasn't handled was the one created by then:

let promise = request(false);

promise.then( response => {          // Creates new promise, rejection isn't handled
  console.log('response' , response);
});

promise.catch( (err) => {            // Creates new promise
  console.log('got Error', err);
});

This is one of the reasons you see promise chains:

request(false)
.then( response => {
  console.log('response' , response);
})
.catch( (err) => {
  console.log('got Error', err);
});

There, three promises are still created (the original from request, the one from then, and the one from catch), but rejection is handled for all three of them by the final catch handler.

The promise created by then and catch work like this:

  • If the underlying promise resolves:
    • If there's no then handler, resolve with the resolution from the original promise
    • If there's a then handler, call it:
      • If the handler returns a thenable (a promise-like object), hook up to it and resolve or reject based on whether that thenable resolves or rejects
      • If the handler returns a non-thenable value, resolve with that value
      • If the handler throws an error, reject with that error
  • If the underlying promise rejects:
    • If there's no catch handler, reject with the rejection from the original promise
    • If there's a catch handler, call it and do exactly what's done with the then handler above (resolve or reject based on what it returns or throws)

promise.then() creates a new promise whose settlement depends on promise. When promise is rejected, the implicit promise created by promise.then() is also rejected and has no catch() clause to handle the error.

What you need to do is chain the .catch() to the promise returned by promise.then():

function request(status){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(status){
        resolve('Success');
      } else {
        reject('error');
      }
    }, 1000);
  }); 
}


let promise = request(false);

promise.then( response => {
  console.log('response' , response);
}).catch( (err) => {
  console.log('got Error', err);
});

Please take a look at following two ways this can be handled.

In first approach you try to handle promise rejection once the promise is resolved within 'then' or else you can chain .then() with .catch().

Also please note that err block within then would be called only if promise is rejected. But catch block is called even if there is any other js error.

promise
  .then(response => {
    console.log("response", response);
  })
  .catch(err => {
    console.log(err);
  });

promise.then(
  response => {
    console.log("response", response);
  },
  err => {
    console.log(err);
  }
);

Code Sandbox Link

Related