How to handle errors using `.catch` in promises - node.js

I want to continue code execution when my promises do not have errors, If there are any errors however, I do not want to proceed. How do I gracefully handle this case ?
Eg.
/* start of REST call */
const resultA = fetchThings()
.then(things => returnSomeResult(things))
.then(...)
...
.catch(err => throw err); // cannot proceed computation if there is error
const resultB = fetchOtherThings()
.then(otherTings => returnOtherResult(otherThings))
.then(...)
...
.catch(err => throw err); // cannot proceed if there is error
const resultC = Promise.all([resultA, resultB]) => { // do other stuff }
// return resultC from REST call if successful
When an error occurs in either of the results, I am getting a UnhandledPromiseRejectionWarning on the terminal. It states that 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 a .catch(). I suspect it is because I am throwing the error inside the catch block.
However, I don't want the code to proceed when there is an error from either resultA or resultB.
How can I better handle this scenario of mine ?

My guess would be that you're not handling errors thrown by Promise.all(), which will be rejected if any of the promises it's handling will be rejected.
So you need to catch errors there as well:
Promise.all(…).then(…).catch(e => …)
FWIW, your catch()'s are superfluous:
.catch(err => throw err)
You're catching errors that were thrown and rethrowing them, which isn't necessary (they will be caught by .catch() that you'll attach to Promise.all()).

Related

Unhandled Promise Rejection: AxiosError: Network Error

Working on a MERN stack project. When trying to access the site on localhost on iPhone Posts are posts are not loading. Working fine on adnroid devices.
Screenshot of error
const fetchFeedPosts = async () => {
const URL = `${BASE__URL}api/posts/feed/${LOGGED__IN__USER}`;
await axios.get(URL)
.then((response) => setFeedPosts([...response.data].reverse()))
.catch((e) => console.log(e.response));
}
fetchFeedPosts()
What the error means
When an Error is thrown (e.g. throw new Error()) it can
be catched locally (e.g. try{...} catch(err) { /** here */ })
or be passed on to the calling function. And in that fashion it bubbles up from caller to caller until it reaches a catch somewhere.
However, if it continues to bubble up, and there's nothing that captures it by the time it reaches the root of your application or code, then that will result in a so-called "Unhandled" error.
Now, as you may know, promises are more like jobs that drift around. They aren't called from the root of your application. But as they bubble up they can also reach a similar root-point, in which case they become "Unhandled Promise rejections".
What to do about it
Unhandled errors or rejections are bad practice though. Errors should be caught somewhere. And without catching them, you can't really know what has caused the error to happen in the first place.
In most cases, you can catch them with a .catch() function, (e.g. yourPromise.catch((err) => {console.err(err)}))
In case your promise is handled in an async function and waited for with an await keyword, then it's slightly different. In that case it makes more sense to use a try-catch block to capture your error.
How to apply it to your code
So, the first way of doing it would be to use the .catch() function
axios.get(URL)
.then((response) => setFeedPosts([...response.data].reverse()))
.catch((err) => console.error(err));
The alternative is to use the await syntax with a try-catch. If you want to use this syntax, you have to put the async keyword before your function.
try {
const response = await axios.get(URL)
setFeedPosts([...response.data].reverse()))
} catch (err) {
console.log(err);
}
Sure, you could mix the 2, but in most cases that would be rather strange.

UnhandledPromiseRejectionWarning Persists Even After Chaining a .catch()

My system consists of an Angular UI and a Node API. The UI submits a file to the API for processing, then gets the result back. This all works - however - the API sometimes fails at processing unexpected data.
I want to be able to catch the error(s) when they arise, stop execution so they won't screw up the UI, then send a message back to UI.
Here is my code so far:
const IncomingForm = require('formidable').IncomingForm;
asynch function myApi(req, res)
{
try // (1)
{
var form = new IncomingForm(...);
form.on('file', async(field, file) =>
{
const [result] = await sometimesBad(inParam); // (2) attach .catch(...);
...
res.send({goodFinal}); // execution should not reach here if error occurs before
});
form.on('end', ()=> {})
form.parse(req)
}
catch (erApi) // (3)
{
... // (4)
}
}
async function sometimesBad(x)
{
try // (5)
{
... lines of code could have run-time error depends on x ...
return goodResult;
}
catch(err2) // (6)
{
... // (7)
}
}
Currently, after hours of searching and trial and error, I:
am able to send a message back by chaining a .catch() at (2)
am unable to stop the execution via any combination of (1), (3), (4), (5), (6), (7), including the use of next(), throw new Error(), await Promise.reject(), return Promise.reject().
am always getting UnhandledPromiseRejectionWarning: Unhandled promise rejection.
Node version: 14.9
Update: In addition to accepted answer, there is no need to have (5), (6), (7).
In your code if (2) throws the error indeed is not handled. To handle it, you need to wrap the code inside async (field, file) => ... into try / catch, similar to how you did on the top level of middleware, and inside the catch you do next(error). Also add default error handler after all routes in your app. See How to return error to the client client without making node server crash? regarding that.
You can stop unhandledRejection(s) from crashing your app by handling them. However, if you fail to handle them using catch blocks, you can also watch for events on the process.
Code Example from the Docs:
process.on('unhandledRejection', (reason, promise) => {
console.log('Unhandled Rejection at:', promise, 'reason:', reason);
// Application specific logging, throwing an error, or other logic here
});
somePromise.then((res) => {
return reportToUser(JSON.pasre(res)); // Note the typo (`pasre`)
}); // No `.catch()` or `.then()`
Alternatively, you can make your sometimesBad function return a Promise, which would cause all errors happening inside the Promise body to be thrown, which can then be handled in the catch block of the caller.

NodeJS: Promise won't catch thrown exceptions

I have a code like this (simplified):
getStreamFor(path) {
// both, remote and local, return a Promise
if(...) { return getRemoteFileStream(path); }
else { return getLocalFileStream(path); }
}
getRemoteFileStream(path) {
// should throw in my case (MyError)
const validPath = validatePath(path);
return readStreamIfValid(validPath);
}
and in the test case:
it('should throw MyError', () => {
return getStreamFor(path)
.then(() => {})
.catch(error => expect(error).to.be.instanceOf(MyError));
});
The problem is, that when the validatePath(path) Method throws (due to invalid path), nothing get caught in the test case promise. The output in my terminal / console is a regular exception as if it was uncaught.
Does anybody have an idea, why the the Promise wouldn't recognize the throw? How can I fix it without probably surrounding the call in the test case with another "try catch" (since the promise should do that for me)?
Maybe there is a general best practise how to structure Promises in order to avoid error swallowings like these?
Thanks for your help!
The problem here is that validatePath() is not part of the promise chain returned by getRemoteFileStream()
One possible solution is the following:
getRemoteFileStream(path) {
return Promise.resolve()
.then(() => validatePath(path))
.then(validPath => readStreamIfValid(validPath));
}
An exception thrown by validatePath() would now be handled in the Promise's catch handler.

How to make node throw an error for UnhandledPromiseRejectionWarning

I'm using nock.back to mock out some API calls. When unexpected calls are made, UnhandledPromiseRejectionWarning gets printed to the console, but my tests pass, and those warnings are easily missed in the rest of the console output. I want an exception thrown instead of a silent error. How do I do this?
The way I use promises is:
function myFunction(){
return new Promise((resolve, reject) -> {
try {
// Logic
resolve(logic)
} catch(e) {
reject('Provide your error message here -> ' + e)
}
})
}
Or !
function myFunction().then( // Calls the function defined previously
logic => { // Instead of logic you can write any other suitable variable name for success
console.log('Success case')
},
error => {
console.log('myFunction() returned an error: ' + error)
}
)
UPD
Have you had a look here? https://nodejs.org/api/process.html#process_event_unhandledrejection
It describes an unhandledRejection event being emitted when you have no catch for a rejection from a Promise and provides the code to catch the WARNING and output it nicely to console.
(copy paste)
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at:', p, 'reason:', reason);
// application specific logging, throwing an error, or other logic here
});
Node.js runs on a single process.

Promise remains unresolved even after handling all cases

I have been writing on a Discord.JS Bot for quite a bit now, and every now and then it seems to throw me a random error / warning in console after executing one of its chat commands
(specifically !clear).
Now, as I already stated, the message I get in my console is a warning, not an actual error, so that's not the main problem I have;
My problem lies in the execution of the command on Discord's side: Because of the unresolved, rejected promise, it will not execute !clear at all, leaving all messages including the command itself behind. Here's a snippet of my code:
if (member.hasPermission("MANAGE_MESSAGES")) {
channel.fetchMessages({ limit: 100 })
.then(messages => {
console.log(`Deleting ${messages.size} messages...`);
channel.bulkDelete(messages).then(res => {}, err => {});
channel.sendEmbed({
// Success Message
}).then(msg => msg.delete(10000), err => console.log(err));
}, err => { console.log(err) })
} else {
channel.sendEmbed({
// Permission Message
}).then(msg => msg.delete(10000), err => { console.log(err) });
}
As you can see, I resolved both the success and the failure state of every Promise, yet I will still see the following warning in console:
(node:14768) Error: Bad Request Sometimes also throws Not Found
-- Stack Trace that only includes internal Node.JS errors --
(node:14768) 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.
-- More Stack Trace not regarding any of my own code --
If any of you need additional code provided to answer the question, feel free to ask me and I will do so. Also, I can't +rep answers yet, but I always appreciate 'em :)
You aren't handling the msg.delete(10000) rejection. You should handle it like this:
channel.sendEmbed({
// Success Message
}).then(msg => msg.delete(10000)).catch(err => console.log(err));

Resources