Why is res.json returning empty? - arrays

When I make a "GET" request from the client to the server the server should make a axios.get() call to a stock API to retrieve data for an array of tickers. When I console.log the results it seems to be working fine but the array doesn't seem to save, like it gets wiped out and comes back to the client as empty. I think I might be messing this up with async/await.
async function currentPrice(ticker) {
const apiURL = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${API_KEY}`;
let price;
await axios.get(apiURL).then(data => {
try {
price = data.data["Global Quote"]["05. price"];
} catch (error) {
console.log(error)
}
})
return price;
};
app.get("/refresh", redirectLogin, (req, res) => {
const {
user
} = res.locals;
var array = [];
connection.query(`SELECT * FROM holdings WHERE user_name = '${user.user_name}' AND quantity > 0`, (err, results) => {
if (err) throw err;
results.forEach(holding => {
currentPrice(holding.ticker).then(data => {
var updatedTicker = {
ticker: holding.ticker,
description: holding.description,
price_acquired: holding.price_acquired,
market_price: data,
delta: parseFloat(this.market_price) - parseFloat(this.price_acquired),
quantity: holding.quantity,
trade_date: holding.date_acquired
}
array.push(updatedTicker);
// console.log(array);
console.log(updatedTicker.market_price)
})
})
res.json(array)
})
})

You are calling res.json(array) before any of your currentPrice().then(...) calls have finished, thus the array is still empty.
There are a number of different ways to solve this. Probably the simplest is to change for .forEach() loop to a plain for loop and then use async/await to serialize each of your calls to currentPrice():
function currentPrice(ticker) {
const apiURL = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${API_KEY}`;
return axios.get(apiURL).then(data => {
try {
return data.data["Global Quote"]["05. price"];
}
catch (error) {
console.log(error);
throw error;
}
});
}
app.get("/refresh", redirectLogin, (req, res) => {
const { user } = res.locals;
connection.query(`SELECT * FROM holdings WHERE user_name = '${user.user_name}' AND quantity > 0`, async (err, results) => {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
try {
const array = [];
for (let holding of results) {
let data = await currentPrice(holding.ticker);
let updatedTicker = {
ticker: holding.ticker,
description: holding.description,
price_acquired: holding.price_acquired,
market_price: data,
delta: parseFloat(this.market_price) - parseFloat(this.price_acquired),
quantity: holding.quantity,
trade_date: holding.date_acquired
}
array.push(updatedTicker);
}
res.json(array);
} catch(e) {
console.log(e);
res.sendStatus(500);
}
});
});
Various changes:
Simplified the currentPrice() function to just return the axios promise directly
Appropriately reject in currentPrice() if there's an error so the caller sees the error.
Add proper error handling (sending an error response), if the db query fails.
Switch .forEach() loop to a for loop so we can use await to serialize the calls to currentPrice() so we can more easily know when they are all done.
Add error handling and sending of an error response if currentPrice() has an error.
Call res.json(array) only after all the now-serialized calls to await currentPrice() have completed.
FYI, a fully complete transformation here would switch to mysql2 so you can use the promise interface for connection.query() rather than the plain callback interface that you are using now. That would allow you to consolidate error handling to one place more easily.

Related

Waiting for POST request to finish before GET request starts (React/Node)

I have been trying for ages to fix this myself but I have given up. I am using React and Node to:
Send text from React to Node
Modify the text in Node
Send the modified text back to React
It all works fine if I click to start the POST request, wait a second, then click a different button to start the GET request, but I am trying to do it all from one command. My problem is that the GET request often finishes first, so my question is: How do I make sure the POST request finishes before the GET request starts.
I have tried this, but to no avail:
postReq = () => {
if(this.state.theUrl.length > 0) {
axios.post('http://localhost:5000/check', {
url: this.state.theUrl
}).then(function(response) {
console.log("Success")
}).catch(function(error) {
console.log(error)});
}
else {
return 1;
}
return "Finished"
}
getReq = () => {
axios.get('http://localhost:5000/check')
.then((getResponse) => {
this.setState({summaryParts: getResponse.data, postResponse: ""})});
};
callApi = async() => {
const result = await this.postReq();
this.getReq();
}
Try having await before get request.
callApi = async () => {
const result = await this.postReq();
await this.getReq();
};
Additionally, you need to update your postReq method as well, in order to await for the axios call returns with resolved promise. Otherwise, return Finished will be called before finishing the post request. (Therefore use async/await for postReq as well)
postReq = async () => {
if (this.state.theUrl.length > 0) {
/* await for the request to be finished */
await axios
.post("http://localhost:5000/check", {
url: this.state.theUrl,
})
.then(function (response) {
console.log("Success");
})
.catch(function (error) {
console.log(error);
});
} else {
return 1;
}
return "Finished";
};
add async and return
postReq = async () => {
if(this.state.theUrl.length > 0) {
return axios.post('http://localhost:5000/check', {
url: this.state.theUrl
}).then(function(response) {
....

How can I use async / await functions?

I have a function as follows:
function getMessages(){
this.removeListener = myFirestore
.collection('messages')
.doc(this.groupChatId)
.collection(this.groupChatId)
.onSnapshot(
snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
this.listMessage.push(change.doc.data())
}
})
},
err => {
console.log(err)
}
)
return this.renderMessages() # my function
}
In above function I need to use this.listMessage array in this.renderMessages() function.For that getMessages() function should execute my firestore query first and then this.renderMessages() function should be called.But without executing firestore query this.renderMessages() is being called.I know this is the issue with async / await functions but I don't know much about those functions.
Now how can I call this.renderMessages() function after the firestore query by using async / await functions?
You can try this type of approach for using async/await.
async function getMessage() {
try {
this.removeListener = await myFirestore
.collection("messages")
.doc(this.groupChatId)
.collection(this.groupChatId)
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === "added") {
this.listMessage.push(change.doc.data());
}
});
});
} catch (error) {
console.log(error);
}
return this.renderMessages();
}
The code above waits for myFirestore and then returns rendermessage. The keyword await makes JavaScript wait until that promise settles and returns its result. Hope this helps. If ypu want to catch error then you can use try..catch block

Axios promise will never resolve

For the life of me, I can never get my Axios.post promise to resolve.
I know that my front end and backend are perfectly connected.
Try/catch blocks to return the resolved promise haven't worked either.
No matter what I do, I can never get inside of my promise.then() function. What am I doing incorrectly in my backend file?
CODE THAT HASN'T WORKED TO RESOLVE THE PROMISE
async handleDateSubmit() {
let resolvedPromise = await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
resolvedPromise
.then(response => {
//I can never get to here.
console.log("Made it inside");
})
.catch(err => console.log(err));
}
//---attempt two----//
async getResolvedPromise() {
try {
return await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
} catch (error) {
console.log(error);
}
}
async handleDateSubmit() {
let resolvedPromise = this.getResolvedPromise();
//work with resolvedPromsie
}
CURRENT CODE
//------------send_info.js front end file----------//
handleDateSubmit() {
Axios.post('http://localhost:3001/get_number_of_dates_from_email', {
email: this.state.user_email_m
})
.then((response) => {
//I can never get to here.
console.log("Made it inside");
})
.catch(err => console.log(err));
}
//---------------server.js backend file---------------//
router.route('/get_number_of_dates_from_email').post(function (req, res) {
//"user_email" is correct in my schema model and "req.body.email" is always what it should be
User.findOne({ user_email: req.body.email }, (err, foundUser) => {
console.log("Inside of findOne()");
if (err) {
return res.send(err);
}
else {
let numDates = foundUser.dates_list.length;
//I always get here and numDates is always correct
console.log("Number of dates: ", numDates);
return res.json({ "numDates": numDates }); //Should I be using res.send()?
}
});
});
It seems like you're confusing promises and resolved promises at times in your code
// Attempt one
async handleDateSubmit() {
try {
let resolvedPromise = await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
// Here resolvedPromise as stated by its name is not a promise anymore, thus you can't use .then()
// You can directly work with resolvedPromise as it contains the response.
} catch (e) {
console.error(e)
}
}
// Attempt two
async getResolvedPromise() {
try {
// Here you're returning the resolved promise, but the async await syntax turn your function into an AsyncFunction object
// This type of function will wrap the return value in a promise if it's not one
return await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
} catch (error) {
console.log(error);
}
}
async handleDateSubmit() {
// Thus you need to await the result of your function
let resolvedPromise = await this.getResolvedPromise();
}

How to wait for nested forEach statements with mongodb queries inside to return function

I am having a hard time figuring out the asynchronous behaviour of nested forEach() functions with node.js. I am not sure where I should create and return a promise and so on.
I have tried wrapping all the logic of the getFeed() function into a return new Promise(), but I think resolve is called before the nested forEach()'s are done.
friends = user_info.friends;
function GetFeed() {
let post_ids = [];
try {
friends.forEach(async function(friend_id) {
const posts = await db.collection('users').findOne(
{ _id: ObjectId(friend_id) },
{ projection: { posts: 1 }}
);
posts.posts.forEach(function(post_id) {
console.log(post_id);
post_ids.push(post_id);
});
});
return new Promise((resolve, reject) => {
resolve(post_ids);
});
} catch (err) {
return res.status(500).json({ error: 'Internal server error, unable to collection post ids from the database.' });
}
}
GetFeed()
.then((post_ids) => {
console.log(post_ids);
return res.status(200).json({ post_ids: post_ids });
})
the console.log() statements reveal that the GetFeed().then() executes before the console.log() statement in the inner forEach loop. I expect that the GetFeed().then() console.log would wait for the nest forEach logic to finish and then console.log the result.
From the docs:
forEach() executes the callback function once for each array element; unlike map() or reduce() it always returns the value undefined.
Meaning you cant wait for it as you can never get a promise to return from it.
You should use a for or while loop or a library like bluebird to iterate over an array and return promises.
You can use a promise array an await them.
friends = user_info.friends;
async function GetFeed() {
let post_ids = [];
let postsPromiseArray = await Promise.all(
friends.map(friend_id => {
return db.collection('users').findOne(
{ _id: ObjectId(friend_id) },
{ projection: { posts: 1 } }
);
})
)
postsPromiseArray.forEach(posts => {
posts.posts.forEach(function (post_id) {
post_ids.push(post_id);
});
})
return post_ids;
}
GetFeed()
.then((post_ids) => {
console.log(post_ids);
return res.status(200).json({ post_ids: post_ids });
})
I think this could help you to get a desired output.
friends = user_info.friends;
async function GetFeed() {
let post_ids = [];
try {
await friends.forEach(async function(friend_id) {
const posts = await db.collection('users').findOne(
{ _id: ObjectId(friend_id) },
{ projection: { posts: 1 }}
);
posts.posts.forEach(function(post_id) {
console.log(post_id);
post_ids.push(post_id);
});
});
return post_ids;
} catch (err) {
return res.status(500).json({ error: 'Internal server error, unable to collection post ids from the database.' });
}
}
var post_ids = GetFeed()
console.log(post_ids);
return res.status(200).json({ post_ids });

handling asynchronous operations in react

I want to achieve the following using React-jsx:
In one file, I have a function that makes a database request and returns some data. In another file, I want to call this function and then process the data. I want to handle this operation preferably using async/await as I have seen it should be possible to do, but I am not against a promise-based solution. Here is what I did:
export async function getData() {
try {
await request
.get(url, (err, res) => {
console.log('1', res.body);
return res.body;
}
} catch(e) {
return e;
}
}
And in the second file, where I call getData():
import { getData } from './path';
async formatData() {
try {
const data = await getData();
// some formatting
console.log('2', data);
} catch(e) {
return e;
}
}
And here is the result I get in the console:
> '2' undefined
suggesting that await hasn't worked. What I am missing?

Resources