push array in const inside .then - javascript

I'm trying to push a value inside a const but its in a .then and it's not working do you know how can I do that ?
I get a value in my console.log(newResult) in my if but the data is not pushed in my const newResult in the return
res.status(200).json(newResult);
.then(function (friends) {
if (friends) {
const newResult = [];
friends.forEach((r) => {
if (r.UserID == userFound.id) {
models.User.findOne({
where: {
id: r.idFriend
}
})
.then(function(userFound) {
newResult.push({
id: r.id,
user: {
id: r.User.id,
email: userFound.email,
username: userFound.username
}
});
console.log(newResult)
})
} else
newResult.push({
id: r.id,
user: {
id: r.User.id,
email: r.User.email,
username: r.User.username
}
});
console.log(newResult)
});
res.status(200).json(newResult);
}
}
every test realised return an empty tab when i go in my if condition

It will never work because, you are doing async calls
models.User.findOne inside forEach.
You'll get results on console.log when async call to database for fetching user is complete.
But before this all happens the forEach is done executing and code hits the line res.status(200).json(newResult); and you see no results from your if condition.
Instead of using this approach go for mongoose populate and populate userObject based userID while finding friends this way you won't have to do async call inside the forEach.
Read about mongoose populate at: http://mongoosejs.com/docs/populate.html

Related

Weird React problem with axios and useState

my problem is about nested axios request. The code is very simple, with first axios.get i am fetching first portion of data and then using the given respons I am trying to fetch data from second endpoint. (code below)
const getData = async () => {
let returnSecoundValue = {}
try {
const response = await axios.get("http://127.0.0.1:8000/firstEndpoint/", {
auth: {
username: "x",
password: "y",
},
});
// console.log(response.data) <--- WORKS;
response.data.forEach(async element => {
const secoundData = await axios.get(`http://127.0.0.1:8000/secoundEndpoint/${element.id}/`, {
auth: {
username: "x",
password: "y",
},
});
returnSecoundValue[secoundData.data.id] = secoundData.data
});
console.log(returnSecoundValue) <--- WORKS;
setMyState({firstEnd: response.data, secoundEnd: empoRet})
} catch (err) {
console.error(err);
}
As in example above when I am finished fetching second data, I save it in my state as an object. The First element of an object is an array of objects, and the second is the object (used as disc).
Up to this point everything is fine, when I try and console.log it, it shows perfectly as I wanted. Problem starts when I'am trying to get the exact data from the second element.
With my fetched data I'am trying to do something like that:
myState.firstEnd.map((element) => {
try{
console.log(myState); (Works - log all values)
console.log(myState.secoundEnd); (Works - log all values of secoundEnd)
console.log(myState.secoundEnd[2]); (Return undefind)
<Component
key={element.id}
isActive={element.active}
title={element.title}
I have tried to do that in few approaches but each time I ended up with the same result. Is there a problem with mounting or is it something else?
Thanks to the comments, I figured out the problem.
This is the solution:
const secoundValue = response.data.map(async element => {
await axios.get(`http://127.0.0.1:8000/secoundEndpoint/${element.id}/`,
{
auth: {
username: "x",
password: "y",
},
});
});
Promise.all(secoundValue).then((res) => {
res.map((e)=>{
secoundEnd[e.data.id] = e.data
})
setMyState({ ads: resp.data, emp: empoRet });
});

Why am I unable to "map" this mongoose collection?

I am trying to map a collection of user documents in mongoDB. Below is my code.
User.getUsers = function() {
return new Promise(async (resolve, reject) => {
try {
let users = await ValidUser.find()
users.map(function(user) {
return {
username: user.username,
email: user.email
}
})
console.log(users)
resolve(users)
} catch {
reject("404")
}
})
}
This code just logs the original stored data and doesn't "map" the array at all.
The map function of an array returns a new array after applying the passed in mapping function to each element. If you assign it back into the users variable, that should correct the issue as the console.log and Promise resolve call will now be using the newly created array:
users = users.map(function(user) {
return {
username: user.username,
email: user.email
}
})
From MDN:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Array.map returns a new array. If you want to use map, you'll need to assign the return array to users.
users = users.map(function(user) {
return {
username: user.username,
email: user.email
}
})
The mapping you want to do can also be done through a projection which is when you specify which fields you want to be returned by the database. In mongoose it might look like this:
let users = await ValidUser.find().select({ username: 1, email: 1, _id: 0 })

Using promises in javascript to allow for unchained updates

I am new to the javascript promise world and i wrote a code that updates my database table based on some params. but i want all these changes to be made at the same time, and i did some research and came across promise.all promise api. and i wanted to know what i was doing wrong or is there a better way to do this?
your help will be most appreciated.
promise code:
try {
syncEntities = async () => {
let saveTakerOrder;
let tradePromises;
// if no makers in the trade or the taker was not completely filled put it in the orders table
saveTakerOrder = await dynamodb
.put({
TableName: process.env.ORDERS_TABLE_NAME,
Item:
makers.length === 0 ||
taker.initial_quantity !== taker.quantity_removed
? taker
: {
...taker,
status: "CLOSED",
orderRate: `${taker.side}#CLOSED#${taker.rate}`,
},
})
.promise();
// if makers exist
if (makers.length > 0) {
tradePromises = makers.map(async (maker) => {
let savedTrade;
let updatedOrder;
// trade entity model
const matchedTrade = {
id: `TRD${uuid()}`,
buy_order: taker.side === "BUY" ? taker.id : maker.id,
sell_order: taker.side === "SELL" ? taker.id : maker.id,
ticker: maker.ticker,
rate: maker.rate,
quantity: maker.quantity_removed,
createdAt: date.toISOString(),
};
// Save the trade to the trades table
savedTrade = await dynamodb
.put({
TableName: process.env.TRADES_TABLE_NAME,
Item: matchedTrade,
})
.promise();
// Update the order in the orders table if quantity is not filled ELSE close it if completely filled
if (maker.quantity_removed !== maker.initial_quantity) {
updatedOrder = await dynamodb
.update({
TableName: process.env.ORDERS_TABLE_NAME,
key: { id: maker.id },
UpdateExpression:
"set quantity_removed = :quantity_removed, quantity_remaining = :quantity_remaining",
ExpressionAttributeValues: {
":quantity_remaining": maker.quantity_remaining,
":quantity_removed": maker.quantity_removed,
},
})
.promise();
} else {
updatedOrder = await dynamodb
.update({
TableName: process.env.ORDERS_TABLE_NAME,
Key: { id: maker.id },
UpdateExpression: "set #status = :status, orderRate = :orderRate",
ExpressionAttributeValues: {
":status": "CLOSED",
orderRate: `${maker.side}#CLOSED#${maker.rate}`,
},
ExpressionAttributeNames: {
"#status": "status",
},
})
.promise();
}
return Promise.all([savedTrade, updatedOrder]);
});
}
return Promise.all([tradePromises, saveTakerOrder]);
};
await Promise.all([syncEntities]);
} catch (error) {
console.error(error);
throw new createError.InternalServerError(error);
}
Please can someone point out what i am doing wrong with this code or help me correct it Thanks?
All you need to do is remove the awaits from the calls to the things you want to include in your Promise.all. The moment you await something it is going to resolve the promise before continuing. If you were to look at the object you get back from await dynamodb.update(...).promise(), for example, you'll notice that you have a DynamoDBUpdateResponse object (at least I think that's the type). But if you remove the await you'd have a Promise<DynamoDBUpdateResponse> object.
You can still get at the results of the promises after you call await Promise.all. Each item in the array will have a record in the resulting array.

Mongodb keeping consistant order when using async

Building a simple chat app, and I'm trying to fetch user messages however the order is not consistent at all when trying to use async.
const msgQuery = await Message.find({ chatId: id }).sort({
createdAt: 1
});
if (msgQuery) {
msgQuery.forEach(async message => {
try {
const postedById = message.postedById;
const nameQuery = await User.findOne({ _id: postedById }).select({
name: 1
});
console.log(nameQuery.name);
if (nameQuery) {
io.to(id).emit(
'server message',
postedById === message.userId ? nameQuery.name : 'Other',
message.text,
postedById === message.userId ? true : false
);
}
} catch (e) {
console.log(e);
}
});
The result of the message.text is ALWAYS in a random order. I tried not using the nameQuery constant and it worked, order was consistent but.. I need to resolve names too.
You cannot guarantee the order that the async functions will complete. Rather than emit inside of the async function, you have to wait for all of the async functions to complete and then emit in order. I think the simplest way to do this would be to use Promise.all with .map. You can also probably optimize this by only querying for user names that you haven't already found -- store them in an object or something like that.
const messages = await Promise.all(msgQuery.map(async message => {
/* query code */
if (nameQuery) {
return {
postedByName,
message: message.text,
postedByUser,
};
}
}));
messages.filter(Boolean).forEach(({ postedByName, message, postedByUser }) => {
io.to(id).emit(
'server message',
postedByName,
message,
postedByUser,
});
});
The messages array will retain the message order, it will just wait until all of the user queries have completed.

Execute Sequelize queries synchronously

I am building a website using Node.js and Sequelize (with a Postgres backend). I have a query that returns many objects with a foreign key, and I want to pass to the view a list of the objects that the foreign key references.
In the example, Attendances contains Hackathon keys, and I want to return a list of hackathons. Since the code is async, the following thing of course does not work in Node:
models.Attendance.findAll({
where: {
UserId: req.user.id
}
}).then(function (data) {
var hacks = [];
for (var d in data) {
models.Hackathon.findOne({
where: {
id: data[d].id
}
}).then(function (data1) {
hacks.append(data1);
});
}
res.render('dashboard/index.ejs', {title: 'My Hackathons', user: req.user, hacks: hacks});
});
Is there any way to do that query in a synchronous way, meaning that I don't return the view untill I have the "hacks" list filled with all the objects?
Thanks!
Use Promise.all to execute all of your queries then call the next function.
models.Attendance.findAll({
where: {
UserId: req.user.id
}
}).then(function (data) {
// get an array of the data keys, (not sure if you need to do this)
// it is unclear whether data is an object of users or an array. I assume
// it's an object as you used a `for in` loop
const keys = Object.keys(data)
// map the data keys to [Promise(query), Promise(query), {...}]
const hacks = keys.map((d) => {
return models.Hackathon.findOne({
where: {
id: data[d].id
}
})
})
// user Promise.all to resolve all of the promises asynchronously
Promise.all(hacks)
// this will be called once all promises have resolved so
// you can modify your data. it will be an array of the returned values
.then((users) => {
const [user1, user2, {...}] = users
res.render('dashboard/index.ejs', {
title: 'My Hackathons',
user: req.user,
hacks: users
});
})
});
The Sequelize library has the include parameter which merges models in one call. Adjust your where statement to bring the Hackathons model into Attendance. If this does not work, take the necessary time to setup Sequelize correctly, their documentation is constantly being improved. In the end, you'll save loads of time by reducing error and making your code readable for other programmers.
Look how much cleaner this can be...
models.Attendance.findAll({
include: [{
model: Hackathon,
as: 'hackathon'
},
where: {
UserId: req.user.id
}
}).then(function (data) {
// hackathon id
console.log(data.hackathon.id)
// attendance id
console.log(data.id)
})
Also..
Hackathon.belongsTo(Attendance)
Attendance.hasMany(Hackathon)
sequelize.sync().then(() => {
// this is where we continue ...
})
Learn more about Sequelize includes here:
http://docs.sequelizejs.com/en/latest/docs/models-usage/
Immediately invoke asynchronous function expression
This is one of the techniques mentioned at: How can I use async/await at the top level? Toplevel await is likely coming soon as of 2021, which will be even better.
Minimal runnable example:
const assert = require('assert');
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'db.sqlite',
});
const IntegerNames = sequelize.define(
'IntegerNames', {
value: { type: DataTypes.INTEGER, allowNull: false },
name: { type: DataTypes.STRING, },
}, {});
(async () => {
await IntegerNames.sync({force: true})
await IntegerNames.create({value: 2, name: 'two'});
await IntegerNames.create({value: 3, name: 'three'});
await IntegerNames.create({value: 5, name: 'five'});
// Fill array.
let integerNames = [];
integerNames.push(await IntegerNames.findOne({
where: {value: 2}
}));
integerNames.push(await IntegerNames.findOne({
where: {value: 3}
}));
// Use array.
assert(integerNames[0].name === 'two');
assert(integerNames[1].name === 'three');
await sequelize.close();
})();
Tested on Node v14.16.0, sequelize 6.6.2, seqlite3 5.0.2, Ubuntu 20.10.

Categories

Resources