async / await handling error in node js mongodb - javascript

I'm using async function in my node, here I'm calling multiple queries with mongodb
(i.e) :
let data1;
let data2;
try{
data1 = await mongoQuery1
}catch {
res.send(error)
}
try{
data2 = await mongoQuery2
}catch {
res.send(error)
}
res.send({ data1, data2 });
I've confused with this flow. So, basically All I want to send to user is both data1 and data2 but what happens if the query1 fails because, it will be obviously falls in the first catch block and send response to the user, and then continue executing the next try block, and send the response again, which is totally wrong. How can I achieve this, what I want is if any error occured in any catch I want to send the error response back to the user
If all the try block succeeds then only I want to send back the success response to the user, How can I achieve this?

It's not really clear what you are trying to do.
If you want to stop and just send the error then you can return after calling res.send to stop the function continuing.
If you want to send the error with the rest of the data then don't call res.send immediately, store the error in data1 and then send it at the end as normal.

Advice to use Promise.all for your scenario:
Both query will be run simultaneously and if there is an error it will immediately throw and got to the catch block.
try {
const [data1, data2] = await Promise.all([mongoQuery1, mongoQuery2]);
res.send({ data1, data2 })
} catch {
res.send(error)
}

Related

async or promise all, reject all if first fails - 3 post method calls

Edited. I need to call addUser() to receive the userId back thats generated from the database. Then i need to call addTicket() with the userId to receive the ticketId back thats generated from the database. Then finally call addToBooks with the userId and ticketId. These are all post methods, the only data I need back from each call are their ids, the last post method will return full data. Is there a way to handle this where I can fail all post methods if any one of the fails in the process? I've seen examples of promise.all that can do this but it was all for get methods.
You could try something like this, following your functions names example:
const getData = async () => {
try {
const userId = await addUser();
if(userId) {
const ticketId = await addTicket(userId);
const response = await addToBooks(userId, ticketId);
return response;
} else {
throw new Error("Error getting data")
}
} catch(error) {
console.log(error);
}
}
This doesn't use Promise.all, but since the example you are giving, to my understanding, only needs the userId to complete the other 2 operations, I think this should work

Execute promises when all resolved (as DB Transactions)

I have two promises that need to act as Transactions, if one of them fails, a "rollback" should occur.
For example: When I create a user I want to send a confirmation email, but what happens if there is an error on sending the email, the user will be created but will never get a confirmation.
await saveUser(user)
await sendEmail(user)
If I switch the order, what happens when something goes wrong on creating the user? It will get a confirmation.
await sendEmail(user)
await saveUser(user)
I thought a Promise.all will do the trick, but it doesn't.
await Promise.all([saveUser(user), sendEmail(user)]);
Is there a way to execute promises as transactions on Javascript? Promises should execute if and only if both of them resolve.
There is no built in 'transaction mechanism' in JavaScript like there is in some databases. You cannot go back in time to undo operations which you have already performed. If you wish to reverse the effects of saveUser when sendEmail fails, then you need to create another function, such as unsaveUser, and invoke it if sendEmail fails.
This can be done in the following way:
Using Promises:
saveUser(user)
.then(() => sendEmail(user))
.catch(err => {
if (err.message === "Email Error") { // Check the exception to determine if the email failed
unsaveUser(user);
}
});
// This will only send the email if 'saveUser' was successful, and call 'unsaveUser' if email fails
Same thing using Async/Await:
try {
await saveUser(user);
sendEmail(user);
} catch(err) {
if (err.message === "Email Error") { // Check the exception to determine if the email failed
unsaveUser(user);
}
}
For this example to work, in your async sendEmail function, you must throw new Error("Email Error"); upon detecting an error. If you are using resolve/reject, it would be reject(new Error("Email Error"));.
I hope this is clear to you and helps you resolve your issue.

Conditionally Sending Response in Catch Block

I'm trying to figure out the proper way to handle a potential bad Fetch response. If the Fetch response is not okay, I want to immediately send a 500 response and and stop the rest of the code from executing. But if the response is okay, I want to continue the program and send a response at the end. My code does seem to work properly at the moment but I get
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
I assume this is because program continues after the catch block and tries to send the second response. Again, the code works but this is obviously not the right way to do it. Is there way I can get the program to stop executing after the response in the catch block is sent?
app.get('/route', async function(req, res){
try{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok)
throw new Error("err")
}catch{
res.status(500).end()
}
/*
Program continues if fetch was successful
*/
res.send("data")
})
Your code is trying to call the res.send("data") even though it sets the response when error occurs inside catch res.status(500).end(). Try returning the response to break the execution of the final response once the response headers are set inside catch as shown below
app.get('/route', async function(req, res){
try{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok)
throw new Error("err")
}catch{
return res.status(500).end()
}
/*
Program continues if fetch was successful
*/
return res.send("data")
})
try-catch is async so don't need to set async function. I use this:
app.get('/route',(req, res)=>{
try
{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok) {
throw new Error("err")
}
}
catch (err)
{
/* handle errors */
}
finally
{
res.status("status code")
// send data and other things ...
res.send("data")
return.end()
}
})

How to do asynchronous calls in vanilla javascript

I managed to use axios by putting this link in my html:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
What I am trying to do is call axios twice, once to call my http request to send in a JSON Object, and another to send in a file along with the JSON object. What I want to do is to make it so that it is required for both to be sent, meaning that the user cannot just send the JSON object by itself or the file by itself. However, my code does not check for that for some reason. I believe it is due to async not being present, But idk how to add that when I am using vanilla js. Here is my code:
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData)
}).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})
For some extra info, my model requires the file and the JSON data being sent.
your second then is in the wrong place. also is formData defined?
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
});
});
a much cleaner way: use async await:
const api = async() => {
try{
const profile = await axios.post('/account/signup', userObj);
const whatever = axios.post('/account/signup/veteranFile-upload', formData);
console.log(whatever)
} catch (e) {
showAppropriateTextSignUp(error.response.data, signupErr)
}
}
you might need to chain the axios call instead of returning. That is
axios.post('/account/signup', userObj)
.then((profile)=>{
//current user info, including if they are verified or not
axios.post('/account/signup/veteranFile-upload', formData).then(resp=>{
//do something with the response
//window.location.replace('/')
}).catch(err=>{
console.log(err)
})
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})

Why doesn't this async function throw undefined?

I have a pretty simple async function that calls the fetch api, and brings me back some data. I am using the await keyword 2 times in this function, and then getting that data and pushing it into my component state.
Here is my pseudo-code in regards to how this function is executing (please tell me if I'm right or wrong here):
Call the fetch api with await: this allows the rest of your code to continue to the next line.
Once you get the fetch the response stream, put it into data variable. Again, the code can continue while we are waiting for this to happen.
Log the data to the console.
Step 3 is where I have some questions...let's say I'm on a really terrible network, and my fetch request doesn't give me my data for 5 full seconds. At that point, shouldn't my console.log(data) line throw undefined, and execute the catch block, due to the async function allowing console.log(data to run BEFORE I get my fetch data back?
I tested this by going into the Chrome Web dev console, and selected the "slow 3g" connection. Even with that network, I was able to log my data to the console without throwing undefined.
What I want to do is to make sure there is data to push into state right after I get my data object back.
getMovieDetails = async id => {
const API_KEY = process.env.REACT_APP_API_KEY;
const movieId = id;
const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${API_KEY}&language=en-US`;
try {
const response = await fetch(url);
const data = await response.json();
console.log(data);
this.setState({
title: data.title,
poster: data.poster_path
});
} catch (err) {
console.log(`Couldn't fetch the endpoint!`);
console.log(err);
}
};
You are wrong in the first point of your Pseudo-code
Call the fetch api with await: this allows the rest of your code to
continue to the next line.
Actually, no. await will block the execution of the next lines in the async function.
So,
yourAsyncFunction = async () => {
await doSomething();
console.log('done something') // Will not run until doSomething() gets completed
}
Which is why you are always getting the fetched data in your console.log(data) statement.

Categories

Resources