async проблема массив пустой nodejs mongoose
Я хочу получить из массива идентификаторов, которые входят в req.body, название и цену каждого из продуктов и сохранить их в новой модели (платеже), для этого я просматриваю каждый продукт и ищу его идентификатор в db (Product.findById), и я сохраняю как объект в arrayProducts то, что мне нужно (имя и цена), но newPayment создается до того, как arrayProducts завершит заполнение, и я создаю его пустым, как я мог бы сделать, чтобы создать newPayment после окончания для каждого продукта?
router.post('/new/payment', async (req, res) => {
const date = Date.now();
const { amount, clientId, products, services, officeId } = req.body;
var arrayProducts = [];
products.forEach(function (product) {
var productFound = {};
Product.findById(product, function (err, result) {
if (err) {
res.status(404).send({ error: "Oh uh, something went wrong", err: err });
}
else {
productFound = { name: result.name, price: result.price };
arrayProducts.push(productFound);
console.log(arrayProducts);
}
})
});
console.log(arrayProducts);
const newPayment = new Payment({ amount, clientId, products: arrayProducts, services, officeId, date });
await newPayment.save((err) => {
if (err) {
res.status(404).send({ error: "Oh uh, something went wrong", err: err });
}
else {
res.status(201).send({ status: "Payment successful", id: newPayment._id });
}
});;
});
Ответы
Вам нужно прочитать о неблокирующих асинхронных операциях в node.js. Ваш Product.findById()является неблокирующим и асинхронным, что означает, что его обратный вызов вызывается LONG после того, как весь ваш обработчик маршрута завершил выполнение и, конечно, после console.log(arrayProducts);и всего кода, который идет после этого.
Поскольку вы хотите выполнить N запросов из своей базы данных и просто хотите знать, когда все они будут выполнены, наиболее эффективным способом сделать это будет использование интерфейса Promise для вашей базы данных, сбор массива обещаний из всех операций с БД. а затем используйте, Promise.all()чтобы сообщить, когда они все будут готовы.
Вот что я бы посоветовал:
router.post('/new/payment', async (req, res) => {
const date = Date.now();
const { amount, clientId, products, services, officeId } = req.body;
try {
const allProducts = await Promise.all(products.map(async (product) => {
const item = await Product.findById(product);
return {name: item.name, price: item.price};
}));
const newPayment = new Payment({ amount, clientId, products: allProducts, services, officeId, date });
await newPayment.save();
res.status(201).send({ status: "Payment successful", id: newPayment._id });
} catch(err) {
res.status(404).send({ error: "Oh uh, something went wrong", err: err });
}
});
В этом коде отсутствуют следующие вещи:
- Проверка входных значений
amount, clientId, products, services, officeIdи отправка соответствующих ошибок, если какие-либо из них отсутствуют или недействительны. - Что делать, если указан товар, которого нет в вашей базе?
- Ошибка 404 может не подходить, если вы получаете ошибку базы данных (вероятно, это ошибка 500).