4

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?

Thamaraiselvam
  • 6,961
  • 8
  • 45
  • 71

3 Answers3

7

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)
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

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);
});
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • How does this then work on browser? I just an the original question and it ran without any error. It gives error on Node though. – Ritesh Waghela Sep 21 '18 at 07:43
  • @Ritesh you sure you ran it correctly? The original question errors for me on the browser as expected. – Patrick Roberts Sep 21 '18 at 07:45
  • 1
    @Ritesh - What browser? All major modern browsers report unhandled rejections now, but if you're using something older (or a polyfill...). (It's very, very hard for polyfills to report unhandled rejections correctly, since it basically has to be done by the garbage collector.) – T.J. Crowder Sep 21 '18 at 07:46
  • I ran it on chrome console.. my chrome version is 68.0 – Ritesh Waghela Sep 21 '18 at 07:48
  • @T.J.Crowder for 99% of cases it would probably be sufficient for polyfill to report error if `.catch()` isn't attached by the time the rejection occurs. – Patrick Roberts Sep 21 '18 at 07:48
  • @PatrickRoberts - Oh, that's not at all true. :-) `Promise.reject().catch(...)` for instance. Which is why modern implementations don't do it that way. – T.J. Crowder Sep 21 '18 at 07:50
  • @T.J.Crowder a proper polyfill would construct a pending promise and reject asynchronously in that case, which would still satisfy the condition I stated. At the very least, the rejection is propagated asynchronously anyway so the `.catch()` is still handled after the current event tick is completed. – Patrick Roberts Sep 21 '18 at 07:52
  • @Ritesh - Chrome definitely reports an unhandled rejection for the OP's code. It just labels it "Error (in promise)", so maybe that threw you? https://i.stack.imgur.com/xYJ40.png – T.J. Crowder Sep 21 '18 at 07:53
  • @PatrickRoberts - Fair point, although that would be a violation of [the spec](https://tc39.github.io/ecma262/#sec-promise.reject). (Not one that can be observed in code, though.) I'm afraid we'll have to agree to disagree that it can be handled correctly even in 99% of cases (or that 99% is good enough :-) ). – T.J. Crowder Sep 21 '18 at 07:54
  • @T.J.Crowder Please see this.. https://ibb.co/cTGWwe – Ritesh Waghela Sep 21 '18 at 07:58
  • 1
    @Ritesh - How long did you wait? The error shows up after a (very brief) delay. Do you have any filtering active in console? In any case, there is an unhandled rejection, it's very odd if your copy of Chrome v68 (which definitely handles this) isn't reporting it for some reason. – T.J. Crowder Sep 21 '18 at 08:07
1

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

Shubham Gupta
  • 2,596
  • 1
  • 8
  • 19