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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With