1

Why does the following code report an Uncaught (in promise) rejected error when it is being caught?

function Test() {
  this.start = function(action) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (action == "fail") return reject("rejected");
        resolve("resolved");
      }, 1000);
    });
  }
}

const test = new Test();
const promise = test.start("fail");
promise.then(console.log);
promise.catch(console.error);

The output (in a browser) is:

rejected from the catch()

Uncaught (in promise) rejected from the call to reject

deceze
  • 510,633
  • 85
  • 743
  • 889
Austin France
  • 2,381
  • 4
  • 25
  • 37
  • if you use `promise.then(console.log).catch(() => {});` there is nothing displayed in the console, you need to chain it or it does not work, like `promise.then(console.log);promise.catch(() => {});` will not work – Lk77 May 19 '22 at 09:27
  • Your need is not very clear. Please tell what you want so we can help you – alexzerah May 19 '22 at 09:33
  • @alexzerah I think it is clear to me. – Ergis May 19 '22 at 09:34
  • I personnaly don't get what you ask. Do you want : - an explanation ? - a correction of code ? – alexzerah May 19 '22 at 09:35

5 Answers5

6

You're forking your promise chain:

promise ---> .then( .. ) ---> ???
        \
         \
          +-> .catch( .. )

The rejection is being caught by the .catch just fine, but it also goes through the .then branch. Both of those branches are resolved independently. And you're not catching the rejection on the .then branch, leading to the uncaught rejection error.

Compare with:

promise.then(console.log).catch(console.error)

Here the chain looks like this:

promise ---> .then( .. ) ---> .catch( .. )

The rejection will skip the .then handler and go to the nearest .catch.

deceze
  • 510,633
  • 85
  • 743
  • 889
2

Ok. The answer is obvious, yet easy to miss.

.then(...) returns another promise. So you have to attach the .catch(...) to THAT.

Solution:

const promise = test.start("fail");
promise.then(console.log).catch(console.error);  

OR more explicitly:

const promise = test.start("fail");
const promise2 = promise.then(console.log);
promise2.catch(console.error);

@deceze's answer/explaination is pretty good.

Ergis
  • 1,105
  • 1
  • 7
  • 18
0

Here is the corrected code :

function Test() {
  this.start = function(action) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (action == "fail") return reject(new Error("rejected"));
        resolve("resolved");
      }, 1000);
    });
  }
}

const test = new Test();
const promise = test.start("fail");
promise.then(console.log).catch(console.error);

you need to chain .then() and .catch() for it to work,

because .then() will return a new promise, and it's that promise that will throw the error you need to catch

deceze
  • 510,633
  • 85
  • 743
  • 889
Lk77
  • 2,203
  • 1
  • 10
  • 15
  • 1
    I think the OP needs the elaboration/explaination, rather than the 'quick fix'. I already new this corrected code which I could've posted in the first minute, but the explaination is more important. – Ergis May 19 '22 at 09:32
  • Yes. I want to know why adding a catch to the top level promise (the one that's being rejected) which does call the catch handler, is reporting that the rejection was unhandled (it clearly was handled - or not given of @deceze's answer). – Austin France May 19 '22 at 10:26
  • Well the second one is unhandled, .then() will return a promise and you will need to catch that one also – Lk77 May 19 '22 at 12:07
0

Because there's no promise made for your .then

Either you change your function call into

promise.then(console.log).catch(console.error);

or change your reject into resolve on your promise

function Test() {
    this.start = function(action) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (action == "fail") return resolve("rejected");
                return resolve("resolved");
            }, 1000);
        });
    }
}

hope this helps

Riku
  • 652
  • 1
  • 9
  • 24
-1

Your code says the following:

const test = new Test();
const promise = test.start("fail");

You create a promise and give a string parameter ("fail").

promise.then(console.log);
promise.catch(console.error);

You catch the promise and show the error

if (action == "fail") return reject("rejected");

You return the reject function with the string parameter "rejected".

Hope it help.

alexzerah
  • 174
  • 1
  • 1
  • 11