Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle exception resulting in unresolved promise

I'm seeing some inconsistent behaviour when dealing with a promise that fails to resolve due to an unforeseen uncaught exception. It seems that depending on how I chain the promise changes whether this promise resolves and I don't understand why.

Here is the bad function:

function bad() {
  return new Promise(resolve => {
    setTimeout(() => {
      throw 'unforseen exception!';
      resolve();
    }, 50);
  });
}

If I call this function these ways it does not resolve:

bad().then(() => console.log('resolved')); // no console logging

try {
  await bad();
} catch(e) {
  console.log(e);
}
console.log('resolved'); // no console logging

But calling it like this does resolve:

Promise.resolve().then(bad()).then(() => console.log('resolved')); // console logs "resolved"

Why is this? Edit: I now understand what I was doing wrong. But what I really want to answer is the next part.

And how do I best protect myself against unforeseen exceptions when I have a chain of promises that need to be run serially and need to continue even if there is a failure somewhere along the chain?

I have also tried using catch or finally but they don't seem to make any difference. Once that unresolved promise is reached, execution fails.

like image 823
Deacon Meek Avatar asked Nov 18 '25 16:11

Deacon Meek


1 Answers

The problem is that bad() throws an error asynchronously in such a way that the error can't be detected by the caller. If you want to throw an error inside a new Promise... segment, you should call the reject function:

function bad() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('bad!');
      resolve();
    }, 50);
  });
}


bad()
  .then(() => console.log('resolved'))
  .catch((err) => console.log(err));

(async () => {
  try {
    await bad();
  } catch(e) {
    console.log(e);
  }
  console.log('await finished');
})();

The reason your

Promise.resolve().then(bad()).then

calls the next .then is because then accepts a function as a parameter, but your bad() is invoking bad in the beginning, while the interpreter is trying to come up with the Promise chain. If you had passed bad as the function parameter instead of calling it, you would see similar broken behavior as in your original code - the Promise would never resolve:

function bad() {
  return new Promise(resolve => {
    setTimeout(() => {
      throw 'unforseen exception!';
      resolve();
    }, 50);
  });
}

// Promise never resolves:
Promise.resolve().then(bad).then(() => console.log('resolved'));

In contrast, .then(bad()) will evaluate to a non-function, and hence that .then will resolve immediately, so the interpreter will go on to the next .then immediately as well.

like image 159
CertainPerformance Avatar answered Nov 20 '25 04:11

CertainPerformance



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!