As https://tc39.es/ecma262/#sec-promise.prototype.catch stated that, Promise.prototype.catch support for catching specified rejection from a Promise. Nevertheless, developers always miss a method, which I have named as Prototype.prototype.uncaught to handle default errors not caught by any handlers.
Similarly, Node has supported unhandledRejection for developers to catch any uncaught error from a Promise.
Such a method can help developers to set up a default error handler for all promises.
(1) Why is it supposed to be placed on the Prototype, if it attaches a handler onto all Promises (as the unhandledRejection event handler does)?
(2) Does handling an error thrown in an async function (rejecting a Promise) differ from throwing in a sync function? Can't the handler handle the general case?
(3) What happens after the handler executed? Does execution end? Or can the handler decide? What happens if a Promise gets rejected (without a handler attached) inside the handler itself?
It depends on whether you want to handle uncaught situation in a middle state or in the end? If in the middle, a prototype method is proper, while a global method is proper in the end.
Take node implementation as an example, unhandledRejection will also be fired in two situations:
For these two examples, we should also trigger 'uncaught' because developers do exactly not catch such exceptions.
My assume is like the following snippet:
Promise.reject(1).uncaught(() => { throw new Error('1 has been uncught'); }); // throw uncaught
Promise.reject(1).catch(result => result > 1 ? Promise.reject(1) : true)
.uncaught(() => { throw new Error('1 has been uncught'); }); // throw uncaught
Promise.reject(1).catch(result => result > 1 ? Promise.reject(1) : true)
.then(console.log) // => true (the reuslt has been caught)
.uncaught(() => { throw new Error('1 has been uncught'); }); // won't throw uncaught
In such a handler, the whole Promise chain should act depend on what it returns:
If you want a default handler does not change the state of a Promise, we can define another two methods like Promise.prototype.onFail or Promise.prototype.onSuccess to support for default successful or failed handling:
Can you maybe give an example of how you envision this uncaught method to be used? It's not quite clear what you are after.
However, it sounds like you are looking to find whether a specific promise got rejected and the error did not get handled anywhere else. This is not possible. Handlers do not "report back" to the promise whether an error got handled or not. Any .catch(), .then() (and by extension await) call simply forwards errors that didn't get handled and will reject another promise with it. The internal state of the original promise is marked as "handled". The unhandeldRejection event only fires for the promises that are the end of a chain.
Sure, in both of those “not fired” cases, the promise is caught/handled. The two catch handlers never throw in those examples, so those promises can never be uncaught, which is why it’s not fired.
My understanding of the use case of the global uncaught rejection handlers is for promises you forgot to handle. If you remembered to do .uncaught, why not just do .catch?
In my opinion, catch is for exact exceptions, while uncaught for unknown or unhandled exceptions. And so far as you said, we just only need a global function for promises we forgot to handle.
Promise.uncaught(
Promise.reject(anyCode),
() => console.log('default handler for any other code')
)
.catch(catchCode(1, () => console.log('catch 1')))
.catch(catchCode(2, () => console.log('catch 2')))
In your first situation, it seems using standard .catch() would have the exact same result as you desire.
For your second situation, you can write your own Promise.uncaught, but it is pretty hacky and will never work in the middle of a chain, especially not with uncaught(somePromise, handler).then(ignore).
It would be necessary, but I don't see why it would be beneficial to add such a method, since .catch already serves this purpose. In other words, while it's perfectly valid for you to choose to see catch as "for exact exceptions", that's not how it's defined nor how it's commonly used.
Like what @bergus said above, standard Promise.prototype.catch can handle the first situation, except detecting unhandled Promise like https://stackoverflow.com/a/57792542.
If I want to register a global default error handler for any promises, I need to override the method Promise.prototype.catch? In such a situation, if I don't want to handle it in one catching block, I need to reject it again to give to default handler?
const fn = Promise.prototype.catch;
Object.assign(Promise.prototype, {
catch() {
if (this instanceof UnhandledPromise) {
// default handling
} else {
return fn.apply(this, arguments);
}
}
});
doSomething().catch(err => {
if (err.code === 1) {
// handler error code 1 here
} else {
// give to a default handler
return Promise.reject(new UnhandledPromise(err));
}
})