Here, productIds has ID of all ordered items in an array like ["1294382", "2913892"]
So I'm trying to get all the items with those ID's from MongoDB's database. I'm mapping through each of them, and store them in orderedItems array. So that i can access price (or any property) of the item in database.
productRouter.post("/", async (req, res) => {
const productIds = req.body.orderedItems.map((item) => item.id);
let orderedItems = [];
productIds.map(async (productId) => {
const d = await Product.findById(productId);
orderedItems.push(d);
});
const unusedVariable = await Product.findById("61...da1"); //If i delete that line (or only await), orderedItems returns empty array.
console.log(orderedItems);
});
Now, expected output is:
[
{
_id: new ObjectId("614632cc8aa9513567dfbca4"),
name: 'ürün1',
desc: 'ürün1 açıklama',
price: 50,
__v: 0
},
{
_id: new ObjectId("614632cc8aa9513567dfbca2"),
name: 'kartal kupa',
desc: 'kartal resimli kupa',
price: 50,
__v: 0
}
]
And I get that output, but only if I have the unusedVariable line with await. If i remove that line, or remove "await" from there, the output i get is: [] . So an empty array instead of filled with objects.
Why ? And how do i solve that (just leave unusuedVariable there?) ? How bad is this code ?
You have a concurrency issue. There is nothing unusual, you are not waiting for promises at (1) to end. The only reason your code is working is that MongoDB in your case uses a single connection and hence (2) is queued and completed only after (1) (all of them) are completed (it is a lucky coincidence of yours).
productRouter.post("/", async (req, res) => {
const productIds = req.body.orderedItems.map((item) => item.id);
let orderedItems = [];
productIds.map(async (productId) => { // (1)
const d = await Product.findById(productId);
orderedItems.push(d);
});
const unusedVariable = await Product.findById("61...da1"); // (2)
console.log(orderedItems);
});
As mentioned all-around nor map nor forEach supports promises (and they probably will never, due to backward compatibility) hence you should either use Promise.all which may give you some concurrence, or use plain for which is compatible with async/await syntax.
productRouter.post("/", async (req, res) => {
const productIds = req.body.orderedItems.map((item) => item.id);
let orderedItems = [];
for (const productId: productIds) {
orderedItems.push(await Product.findById(productId));
}
console.log(orderedItems);
});
Or
productRouter.post("/", async (req, res) => {
const productIds = req.body.orderedItems.map((item) => item.id);
const orderedItems = await Promise.all(
productIds.map(productId => Product.findById(productId))
);
console.log(orderedItems);
});
As mentioned in the comments you should consider using $in operator, which will drastically reduce the number of queries against your DB:
productRouter.post("/", async (req, res) => {
const productIds = req.body.orderedItems.map((item) => item.id);
const orderedItems = Product.find({_id: {$in: productIds}});
console.log(orderedItems);
});