Promise reject() causes "Uncaught (in promise)" warning

JavascriptPromiseEs6 PromiseCatch Block

Javascript Problem Overview


Once a promise reject() callback is called, a warning message "Uncaught (in promise)" appears in the Chrome console. I can't wrap my head around the reason behind it, nor how to get rid of it.

var p = new Promise((resolve, reject) => {
  setTimeout(() => {
    var isItFulfilled = false
    isItFulfilled ? resolve('!Resolved') : reject('!Rejected')
  }, 1000)  
})

p.then(result => console.log(result))
p.catch(error => console.log(error))

Warning:

enter image description here

Edit:

I found out that if the onRejected handler is not explicitly provided to the .then(onResolved, onRejected) method, JS will automatically provide an implicit one. It looks like this: (err) => throw err. The auto generated handler will throw in its turn.

Reference:

> If IsCallable(onRejected)` is false, then
>      Let onRejected be "Thrower".

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-performpromisethen

Javascript Solutions


Solution 1 - Javascript

This happens because you do not attach a catch handler to the promise returned by the first then method, which therefore is without handler for when the promise rejects. You do have one for the promise p in the last line, but not for the chained promise, returned by the then method, in the line before it.

As you correctly added in comments below, when a catch handler is not provided (or it's not a function), the default one will throw the error. Within a promise chain this error can be caught down the line with a catch method callback, but if none is there, the JavaScript engine will deal with the error like with any other uncaught error, and apply the default handler in such circumstances, which results in the output you see in the console.

To avoid this, chain the .catch method to the promise returned by the first then, like this:

p.then( result =>  console.log('Fulfilled'))
 .catch( error =>  console.log(error) );

Solution 2 - Javascript

Even if you use Promises correctly: p.then(p1).catch(p2) you can still get an uncaught exception if your p2 function eventually throws an exception which you intend to catch using a mechanism like window.onerror. The reason is that the stack has already been unwound by the error handling done in the promise. To fix this, make sure that your error code (called by the reject function) does not throw an exception. It should simply return.

It would be nice if the error handling code could detect that the stack has already been unwound (so your error call doesn't have to have a flag for this case), and if anyone knows how to do this easily I will edit this answer to include that explanation.

Solution 3 - Javascript

This code does not cause the "uncaught in promise" exception:

// Called from top level code; 
// implicitly returns a Promise
testRejectCatch = async function() {

    // Nested within testRejectCatch;
    // simply rejects immediately
    let testReject = function() {
        return new Promise(function(resolve, reject) {
            reject('test the reject');
        )};
    }
 
//***********************************************
// testRejectCatch entry.
//***********************************************
try {
    await testReject(); // implicitly throws reject exception
catch(error) {
    // somecode 
 }

//***********************************************
// top level code
//***********************************************
try{
    testRejectCatch()   // Promise implicitly returned,
    .catch((error) => { // so we can catch
        window.alert('Report error: ' + error);
       // must not throw error;
    });
}
catch(error) {
    // some code
}

Explanation: First, there's a terminology problem. The term "catch" is used in two ways: in the try-catches, and in the Promises. So, it's easy to get confused about a "throw"; is it throwing to a try's catch or to a Promise's catch?

Answer: the reject in testReject is throwing to the Promise's implicit catch, at await testReject; and then throwing on to the .catch at testRejectCatch().

In this context, try-catch is irrelevant and ignored; the throws have nothing to do with them.

The .catch at testRejectCatch satisfies the requirement that the original throw must be caught somewhere, so you do not suffer the "uncaught in Promise..." exception.

The takeaway: throws from Promises are throws to .catch, not to try-catch; and must be dealt-with in some .catch

Edit: In the above code, the reject propagates up through the .catches. If you want, you can convert over to propagating up the try-catches. At line 17, change the code to:

let bad = '';
await testReject().catch((error) => {bad = error});
if (bad) throw bad;

Now, you've switched over to the try-catch.

Solution 4 - Javascript

I ran into this issue, but without setTimeout().

In case anyone else runs into this: if in the Promise constructor you just call reject() synchronously, then it doesn't matter how many .then() and .catch() handlers you add to the returned Promise, they won't prevent an uncaught promise rejection, because the promise rejection would happen before you

Solution 5 - Javascript

I've solved that problem in my project, it's a large enterprise one. My team is too lazy to write empty catch hundreds of times.

Promise.prototype.then = function (onFulfilled, onRejected) {
    return baseThen.call(this, (x: any) => {
        if (onFulfilled)
            onFulfilled(x);
    }, (x: any) => {
        if (onRejected)
            onRejected(x);
    });
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYevgeny KozlovView Question on Stackoverflow
Solution 1 - JavascripttrincotView Answer on Stackoverflow
Solution 2 - JavascriptDavid SpectorView Answer on Stackoverflow
Solution 3 - JavascriptBinCodinLongView Answer on Stackoverflow
Solution 4 - JavascriptAttila SzeremiView Answer on Stackoverflow
Solution 5 - JavascriptstbearView Answer on Stackoverflow