I am using @typescript-eslint/no-floating-promises
rule in my project. This rule complains if I write code like this -
functionReturningPromise()
.then(retVal => doSomething(retVal));
It wants me to add a catch
block for this Promise
. It makes sense to me if I want some logic in my exception handling block. However, in many cases I don't need that. I am fine with the error being thrown. So, to suppress the error fired by this rule I just end up doing this -
functionReturningPromise()
.then((retVal) => doSomething(retVal))
.catch((error) => {
throw error;
});
Even if I don't add the catch
block as above, the error
still gets thrown the same way (at least as I see it in my console output). So, I don't get the point of explicitly specifying this catch
block. Am I missing something? Is there really a difference in the way the error
is thrown if I add/not add the catch
block?
The commenters have all done a good job of answering your question - but to show why this matters by example, imagine the following code:
Promise.reject();
setTimeout(() => console.log('hello'), 1000);
This code looks pretty innocuous - there is a no-op promise rejection which is unhandled and 1 second after startup the program will log 'hello'
.
In a browser - this is exactly what will happen - the browser will log an "uncaught promise rejection" error but it will otherwise ignore it.
However in NodeJS (as of Node v15) unhandled promise rejections are a HARD ERROR - meaning that the process will exit when it detects one!
You can verify this by running the code in your terminal (-e
means "evaluate and run this code string")
$ node -e "Promise.reject(); setTimeout(() => console.log('hello'), 1000)"
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: 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(). The promise rejected with the reason "undefined".] {
code: 'ERR_UNHANDLED_REJECTION'
}
Node.js v18.12.1
You can see that the 'hello'
never gets printed because the process terminates before 1s!
To validate that things work as expected, you can change the .reject
to .resolve
:
$ node -e "Promise.resolve(); setTimeout(() => console.log('hello'), 1000)"
hello
So if you're writing a NodeJS application with any LTS supported version you definitely should be handling the errors or else you risk unexpected crashes.
If your code only runs in browsers then you're probably wondering if you should handle the failure - after all your users aren't looking at the console so they won't know anything bad happened so who cares? And you'd be right from a "code only" point of view - however your users want feedback from their application when something goes wrong.
Imagine the following scenario - your promise tracks the result of an API call that submits some data the user entered in your application. If that API call fails for some reason, then your application should respond to that appropriately and tell the user something went wrong.
The alternative of handling it could mean your application shows an infinite loading spinner, or that a user thinks their data was submitted successfully when it actually wasn't. Either way - it's a very bad and broken UX!
Finally doing something like .catch(e => { throw e })
you're not actually handling the error. Sure this code silences the linter - but it all you're doing is creating a new, rejected promise which will get logged to the console. Instead you should wire the error into your application's UI in some way, eg something as simple as .catch(e => { alert(e); throw e })
would even be better.