Why is this use of Promise.finally leading to uncaught exception? - javascript

I am implementing a function called timeout to convert a promise into a promise that will reject if not settled within a minimum amount of time.
With the following implementation, I get an uncaught (in promise) message in Chrome devtools when the promise passed to timeout rejects before the allotted time.
function timeout(promise, duration, message) {
const timeoutPromise = new Promise((_, reject) => {
const handle = setTimeout(() => {
reject(new TimeoutError(message ?? "Operation timed out."))
}, duration)
promise.finally(() => clearTimeout(handle)) //problem line
})
return Promise.race([promise, timeoutPromise])
}
If I change it to the following however, I seem to have no issues.
function timeout(promise, duration, message) {
const timeoutPromise = new Promise((_, reject) => {
const handle = setTimeout(() => {
reject(new TimeoutError(message ?? "Operation timed out."))
}, duration)
const clear = () => clearTimeout(handle)
promise.then(clear).catch(clear)
})
return Promise.race([promise, timeoutPromise])
}
I'm trying to understand how calling then & catch in the second scenario is different to calling finally in the first.

This is because finally handlers are different from then and catch handlers. If the promise is rejected, a finally handler can't change rejection to resolution. Like the finally block of a try/catch/finally structure, it's meant to be largely transparent.
Here's how I describe it in my new book (Chapter 8):
In the normal case, a finally handler has no effect on the fulfillment or rejection passing through
it (like a finally block): any value it returns other than a thenable that is/gets rejected is
ignored. But if it throws an error or returns a thenable that rejects, that error/rejection supersedes
any fulfillment or rejection passing through it (just like throw in a finally block). So it can't change
a fulfillment value — not even if it returns a different value — but it can change a rejection reason into a
different rejection reason, it can change a fulfillment into a rejection, and it can delay a fulfillment (by
returning a thenable that is ultimately fulfilled).
Or to put it another way: it's like a finally block that can't contain a return statement. (It might
have a throw or might call a function or do some operation that throws, but it can't return a new value.
Here's an example of not being able to convert rejection to fulfillment:
Promise.reject(new Error("Failed"))
.finally(() => {
console.log("finally handler returns null");
return null;
});
Look in the browser's real console to see the unhandled rejection error.
Another way to deal with it in your code is to do this in place of the problem line:
promise.catch(() => {}).finally(() => clearTimeout(handle))
or
promise.catch(() => {}).then(() => clearTimeout(handle))
That converts rejection to fulfillment (only on this branch) prior to the finally (or then) handler.

Related

promise catch: how to know if the error comes from promise rejection or from the then statement

Imagine a function that returns a promise (for example fetch from fetch API):
fetch(...)
.then((response) => {...})
.catch((error) => {...});
How can I know if the error inside the catch statement has been originated due to a promise rejection instead from the code inside the then statement?
Something like the following will work:
let success = false;
fetch(...)
.then((response) => {success = true; ...})
.catch((error) => {if(success) ...});
But I want to know if there is a better or more native way to do it. I have also tried to do something like:
fetch(...)
.catch((promiseError) => {...});
.then((response) => {...})
.catch((thenError) => {...});
But I think it doesn't work because response isn't forwarded to the thenstatement (or at least that's what TS says).
Which would be the better way?
then accepts two parameters¹: A function to call on fulfillment, and another to call on rejection. So you can handle the initial rejection earlier:
fetch(/*...*/)
.then(
// Fulfillment handler
(response) => {
/*...*/
},
// Rejection handler for `fetch` promise
(error) => {
/*...*/
}
)
.catch((error) => {
// This rejection handler only sees errors not handled above
});
The first rejection handler (in the then call) is only called for rejections of the promise from fetch, not rejections caused by errors in the fulfillment handler.
The second rejection handler (in the catch call) is called for rejections caused by errors (or rejected promises) from the handlers that come before it (either fulfillment or rejection); it doesn't get called if the original promise is rejected (but it may well get called if the first rejection handler throws an error or returns a promise that is/will be rejected).
So in those cases where you care, you can handle it closer to the source.
Note that all rejection handlers that don't either throw an error or return a promise that is/will be rejected convert rejection (of the original promise) into fulfillment (of the one from then/catch). That can matter for downstream then handlers.
A key thing to understanding this is to remember that then (and its wrappers catch and finally) return a new promise. That's why:
aPromise.then(onFulfilled).catch(onRejected);
is different from
aPromise.then(onFulfilled, onRejected);
The first one hooks up a fulfillment handler to aPromise, and then hooks up a rejection handler to the promise then returns (which means the rejection handler will be called if aPromise rejects or if the fulfillment handler throws an error or returns a promise that rejects). In contrast, the second one only hooks up handlers on aPromise, so errors/rejections from the fulfillment handler do not trigger the rejection handler.
¹ In fact, .catch(fn) is just a wrapper for .then(undefined, fn). :-) spec link
if the function pass to then returns a promise,
promise
.then(
(async (response)=>{
...
}).catch((errorFromThen) => {})
).catch((errorFromPromise) => {})
or just simply
promise
.then(
(response)=>{
try {
...
} catch (errorFromThen) {
...
}
}
).catch((errorFromPromise) => {})

How to handle an unhandled promise rejection asynchronously?

I'm trying to wrap my head around this issue I'm facing concerning async/await and Promises. I managed to boil my issue down to the following code:
async function sleep(ms: number) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(): Promise<any> {
return new Promise(() => {
throw 'error fetching result';
});
}
async function main(): Promise<any> {
const kickedOffRequest = fetchMock();
await sleep(10);
return kickedOffRequest;
}
main()
.then(() => console.log('resolved promise!'))
.catch(error => console.error('caught error!', error));
I receive the following warning:
(node:82245) UnhandledPromiseRejectionWarning: error fetching result
(node:82245) 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:82245) [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.
caught error! error fetching result
(node:82245) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
You can observe the same issue in this sandbox. I noticed that commenting out the await sleep(10) fixes the issue, but I apparently know less about promises than I thought. Why does commenting that line out make my program work? I'm tempted to ask how to fix the Promise rejection was handled asynchronously error, but I hope that once I understand how await sleep(10) causes the error I get I will be able to fix this one on my own.
Thanks in advance for taking the time to read/answer this question!
The original concept of promises was that you could have a rejected promise sitting around for some time before attaching a catch handler to it. For example, Firefox used to warn of uncaught rejection errors only when a rejected promise with no rejection handler was garbage collected from memory.
Somebody decided that programmers couldn't be trusted with managing promise rejections properly and changed the HTML spec to require browsers to throw "unhandled promise rejection" errors if a rejected promise has no rejection handlers added before code returns to the event loop.
(I think unhandled rejections can survive without error in the micro task queue for a tick or two, before control returns to the event loop proper, but haven't tested it lately.)
The ECMAScript specification added an abstract means of notifying the host environment of an unhandled promise rejection without specifying what, if any, action should be taken.
On a case by case basis you can prevent the host being notified by adding a rejection handler that is never used. The reasoning is that adding a dummy rejection handler to a promise means that should it be rejected it has a rejection handler already - or if it was rejected the host is notified the promise now has a rejection handler - and you can call then and catch multiple times on the same promise.
Changing
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
to
async function fetchMock(){
let promise = new Promise(() => {
throw 'error fetching result';
});
promise.catch(()=>null); // unused rejection handler
return promise;
}
should work around the unwanted HTML5 host behavior implemented in V8, the JavaScript engine used in node.
The detection of unhandled rejection in node.js is imperfect. There are specific spots in the life cycle of a rejected promise where the engine checks to see if there's a handler and it does not always wait until the last possible moment so it can miss places that you add a handler. In your specific case, you may need to attach a .catch() handler locally, then finish up the work you want to do, then rethrow the error. This work-around will work for you while still maintaining the desired resolve/reject from main() (e.g. without changing the interface to main).
So, this isn't particularly super pretty, but it meets the spec we talked about in comments.
main() calls fetchMock()
If it resolves or rejects quickly (before some custom delay time), then it holds off on the resolve or the reject until at least that delay time has elapsed from when fetchMock() was originally called.
If fetchMock() takes longer than that custom delay time to resolve or reject, then no further delay is added.
The promise that main() returns then follows the promise that fetchMock() returned, either rejected or resolved with the same reason or value.
The key ingredient is that it captures the time right before calling fetchMock() and then when fetchMock() either resolves or rejects, it decides whether to delay any more time before passing the resolve/reject value/reason on through.
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
function fetchMock() {
return new Promise((resolve) => {
throw 'error fetching result';
//resolve('this is our result');
});
}
function handler(start, minWaitTime, isErr = false) {
return async function(val) {
let diff = minWaitTime - (Date.now() - start);
if (diff > 0) {
await sleep(diff);
}
if (isErr) {
throw val;
} else {
return val;
}
}
}
function main() {
let start = Date.now();
const minWaitTime = 1000;
return fetchMock().then(handler(start, minWaitTime), handler(start, minWaitTime, true));
}
main()
.then(() => console.log('resolved promise!'))
.catch(error => console.error('caught error!', error));
Note, also that sleep() and fetchMock() already directly return promises and don't use await so there is no requirement for them to be async.
The problem is that the fetchMock rejects immediately, and when a Promise rejects, at the time that it rejects, it must be chained with a .catch somewhere in order to prevent an Unhandled Promise Rejection.
With your await sleep(10), the kickedOffRequest promise rejects while the main function is still waiting for sleep to resolve. When there's a rejection, the interpreter doesn't look ahead to see if the Promise may be caught in the future (for example, to see if the Promise gets returned or caught) - the Promise must be caught now.
When you remove the await, kickedOffRequest still becomes a rejected Promise, but it's returned from main immediately, so at the point that the Promise rejects, it can be seen and caught by the outer .catch, so there's no Unhandled Rejection warning.
It's good if a Promise rejection can be handled right when it rejects, but if that's not an option for you, you can put another .catch onto the end of the inner Promise. That way, the warning won't appear (because of the existence of .catch, even if that .catch didn't do anything meaningful) and you can later check to see if an error actually occurred or not:
async function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
async function main() {
const kickedOffRequest = fetchMock()
.then(resolved => ({ resolved }))
.catch(rejected => ({ rejected }));
await sleep(10);
return kickedOffRequest;
}
main()
.then(({ resolved, rejected }) => {
if (resolved) {
console.log('resolved promise!');
} else {
console.error('caught error!', rejected);
}
});
This is pretty similar to how Promise.allSettled works (though it's more intended for when there's an array of Promises that need to be parsed):
async function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
async function main() {
const kickedOffRequestArr = Promise.allSettled([fetchMock()]);
await sleep(10);
return kickedOffRequestArr;
}
main()
.then(([{ result, reason, value }]) => {
if (result === 'fulfilled') {
console.log('resolved promise!', value);
} else {
console.error('caught error!', reason);
}
});

What is actually happening when you omit .catch from JavaScript Promises?

The following two functions behave identically when called.
function func1() {
return asyncFunc()
.then(() => {
// do something
});
}
function func2() {
return asyncFunc()
.then(() => {
// do something
}).catch((err) => {
throw err;
});
}
I know that .catch() is merely syntactic sugar for Promise.prototype.then(undefined, onRejected) per MDN docs. However, I'm confused as to what actually occurs behind the scenes when you omit .catch from a promise chain.
What is actually going on behind the scenes when there is no .catch() in a promise chain? Is a .catch((err) => { throw err;}); being "magically" appended somehow?
The .catch((err) => { throw err;}) would not do anything, it would just re throw the error. So the Promise returned by .catch will be rejected with the error err again:
Promise.reject(new Error('test'))
.catch(err => {
console.error(err)
throw err
})
.catch(err => {
console.error(err)
})
func1 would return a Promise that might be rejected by some event that happens in that chain. So the caller of the func1 might want to handle that error, or if the the caller passes the received Promise further, and does not want to handle that error, then also the caller can omit the catch.
But the one "owning" the chain (the one who received it last and does not pass it to anyone else), is responsible to handle the rejection case.
function func1() {
return asyncFunc()
.then(() => {
// do something
});
}
function callerA() {
return func1()
}
function callerB() {
// callerB does not return the promise retuirned by callerA,
// and does not pass it to any other function, so it has to handle
// the rjection case
callerA().catch(err => {
})
}
On a rejection the js environment will check if there this error is catched anywhere in the chain. If that is not the case then a UnhandledPromiseRejectionWarning might be thrown:
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)
Or in case of the browser the error is logged in the console.
How the js enviroment deals with unhandled rejections depends, nodejs currently emits this warning:
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.
And as it says will terminate the application in future versions.
For the browser it is unlikely that it will terminate the tab future. It most likely will always only log that error in the console, but you still should write your code as if the unhandled rejection would terminate the context.
The two examples are definitely functionally equivalent, there is no magic happening behind the scenes. The Promise handler callbacks are designed to automatically handle values that are thrown and apply them to the promise chain.
Consider the following example:
Promise.resolve("{") // broken JSON
.then((json) => {
return JSON.parse(json) // Throws SyntaxError
})
.catch((err) => {
throw err; // Throws the same SyntaxError
})
.catch((err) => {
console.error(err); // Logs the SyntaxError
});
Most Promise implementations have a feature that logs the error when you have not attached an error handler to a promise that was rejected, some even allow you to register a custom event handler (e.g. Chrome and NodeJS) when an unhandled rejection occurs. But not all runtimes are created equal, the Promise implementation in Edge, for instance, does not provide such feature for notifying users of unhandled rejections.
Q: Is a .catch((err) => { throw err;}); being "magically" appended somehow?
A: NO, NOT AT ALL!
The .then method itself returns a promise. And, if you call a .catch method on that returned promise the handler passed in the catch would get called if that promise got rejected. The handler would simply get ignored if the promise got resolved. Read more about error handling in JS promises here.
And, as I already mentioned, the catch method doesn't magically get appended. Your code would just get ignore if the promise returned by .then method would get rejected because you didn't catch it. Run the following code for a better understanding:
var myPromise = new Promise(function(resolve, reject) {
setTimeout(reject, 100);
});
myPromise
.then(() => console.log('hi')); // Prints nothing
myPromise
.then(() => console.log('hi'))
.catch(() => console.log('hello')); // Prints hello

How to throw an exception in the Promise catch method in JavaScript? [duplicate]

Why can't I just throw an Error inside the catch callback and let the process handle the error as if it were in any other scope?
If I don't do console.log(err) nothing gets printed out and I know nothing about what happened. The process just ends...
Example:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
If callbacks get executed in the main thread, why does the Error get swallowed by a black hole?
As others have explained, the "black hole" is because throwing inside a .catch continues the chain with a rejected promise, and you have no more catches, leading to an unterminated chain, which swallows errors (bad!)
Add one more catch to see what's happening:
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // Where does this go?
}).catch(function(err) {
console.log(err.stack); // It goes here!
});
A catch in the middle of a chain is useful when you want the chain to proceed in spite of a failed step, but a re-throw is useful to continue failing after doing things like logging of information or cleanup steps, perhaps even altering which error is thrown.
Trick
To make the error show up as an error in the web console, as you originally intended, I use this trick:
.catch(function(err) { setTimeout(function() { throw err; }); });
Even the line numbers survive, so the link in web console takes me straight to the file and line where the (original) error happened.
Why it works
Any exception in a function called as a promise fulfillment or rejection handler gets automatically converted to a rejection of the promise you're supposed to return. The promise code that calls your function takes care of this.
A function called by setTimeout on the other hand, always runs from JavaScript stable state, i.e. it runs in a new cycle in the JavaScript event loop. Exceptions there aren't caught by anything, and make it to the web console. Since err holds all the information about the error, including the original stack, file and line number, it still gets reported correctly.
Important things to understand here
Both the then and catch functions return new promise objects.
Either throwing or explicitly rejecting, will move the current promise to the rejected state.
Since then and catch return new promise objects, they can be chained.
If you throw or reject inside a promise handler (then or catch), it will be handled in the next rejection handler down the chaining path.
As mentioned by jfriend00, the then and catch handlers are not executed synchronously. When a handler throws, it will come to an end immediately. So, the stack will be unwound and the exception would be lost. That is why throwing an exception rejects the current promise.
In your case, you are rejecting inside do1 by throwing an Error object. Now, the current promise will be in rejected state and the control will be transferred to the next handler, which is then in our case.
Since the then handler doesn't have a rejection handler, the do2 will not be executed at all. You can confirm this by using console.log inside it. Since the current promise doesn't have a rejection handler, it will also be rejected with the rejection value from the previous promise and the control will be transferred to the next handler which is catch.
As catch is a rejection handler, when you do console.log(err.stack); inside it, you are able to see the error stack trace. Now, you are throwing an Error object from it so the promise returned by catch will also be in rejected state.
Since you have not attached any rejection handler to the catch, you are not able to observe the rejection.
You can split the chain and understand this better, like this
var promise = do1().then(do2);
var promise1 = promise.catch(function (err) {
console.log("Promise", promise);
throw err;
});
promise1.catch(function (err) {
console.log("Promise1", promise1);
});
The output you will get will be something like
Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }
Inside the catch handler 1, you are getting the value of promise object as rejected.
Same way, the promise returned by the catch handler 1, is also rejected with the same error with which the promise was rejected and we are observing it in the second catch handler.
I tried the setTimeout() method detailed above...
.catch(function(err) { setTimeout(function() { throw err; }); });
Annoyingly, I found this to be completely untestable. Because it's throwing an asynchronous error, you can't wrap it inside a try/catch statement, because the catch will have stopped listening by the time error is thrown.
I reverted to just using a listener which worked perfectly and, because it's how JavaScript is meant to be used, was highly testable.
return new Promise((resolve, reject) => {
reject("err");
}).catch(err => {
this.emit("uncaughtException", err);
/* Throw so the promise is still rejected for testing */
throw err;
});
According the spec (see 3.III.d):
d. If calling then throws an exception e,
a. If resolvePromise or rejectPromise have been called, ignore it.
b. Otherwise, reject promise with e as the reason.
That means that if you throw exception in then function, it will be caught and your promise will be rejected. catch don't make a sense here, it is just shortcut to .then(null, function() {})
I guess you want to log unhandled rejections in your code. Most promises libraries fires a unhandledRejection for it. Here is relevant gist with discussion about it.
Yes promises swallow errors, and you can only catch them with .catch, as explained more in detail in other answers. If you are in Node.js and want to reproduce the normal throw behaviour, printing stack trace to console and exit process, you can do
...
throw new Error('My error message');
})
.catch(function (err) {
console.error(err.stack);
process.exit(0);
});
I know this is a bit late, but I came across this thread, and none of the solutions were easy to implement for me, so I came up with my own:
I added a little helper function which returns a promise, like so:
function throw_promise_error (error) {
return new Promise(function (resolve, reject){
reject(error)
})
}
Then, if I have a specific place in any of my promise chain where I want to throw an error (and reject the promise), I simply return from the above function with my constructed error, like so:
}).then(function (input) {
if (input === null) {
let err = {code: 400, reason: 'input provided is null'}
return throw_promise_error(err)
} else {
return noterrorpromise...
}
}).then(...).catch(function (error) {
res.status(error.code).send(error.reason);
})
This way I am in control of throwing extra errors from inside the promise-chain. If you want to also handle 'normal' promise errors, you would expand your catch to treat the 'self-thrown' errors separately.
Hope this helps, it is my first stackoverflow answer!
Listen for unhandled errors:
window.addEventListener('unhandledrejection', e => {
// ...
});
window.addEventListener('error', e => {
// ...
});
If error gets swallowed, use self.report(error):
.catch(error => {
self.reportError(error);
});
https://developer.mozilla.org/en-US/docs/Web/API/reportError

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

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:
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
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) );
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.
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.
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
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);
});
};

Categories

Resources