async problem tablica pusta nodejs mongoose
chcę uzyskać z tablicy identyfikatorów, które znajdują się w żądaniu, nazwę i cenę każdego z produktów i zapisać je w nowym modelu (płatność), w tym celu przeglądam każdy produkt i szukam jego identyfikatora w db (Product.findById) i zapisuję jako obiekt w arrayProducts to, czego potrzebuję (nazwa i cena), ale nowaPłatność jest tworzona przed zakończeniem wypełniania tablicyProdukty i tworzę ją pustą, tak jak mogłem to zrobić, aby została utworzona nowa płatność po zakończeniu forEach z produktów?
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 });
}
});;
});
Odpowiedzi
Musisz poczytać o nieblokujących, asynchronicznych operacjach w node.js. Twój Product.findById()nie blokuje i jest asynchroniczny, co oznacza, że jego wywołanie zwrotne jest wywoływane LONG po zakończeniu wykonywania całej procedury obsługi trasy i na pewno po console.log(arrayProducts);całym kodzie, który nastąpi później.
Ponieważ chcesz wykonać N żądań ze swojej bazy danych i po prostu chcesz wiedzieć, kiedy wszystkie są zakończone, najskuteczniejszym sposobem byłoby użycie interfejsu Promise dla Twojej bazy danych, zebranie tablicy obietnic ze wszystkich operacji DB a następnie użyj, Promise.all()aby poinformować, kiedy wszystko jest gotowe.
Oto, co proponuję:
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 });
}
});
Inne rzeczy, których brakuje w tym kodzie to:
- Sprawdzanie poprawności wartości wejściowych
amount, clientId, products, services, officeIdi wysyłanie odpowiednich błędów, jeśli brakuje któregokolwiek z nich lub jest on nieprawidłowy. - Co zrobić, jeśli określono produkt, którego nie ma w Twojej bazie danych?
- Błąd 404 może nie być odpowiedni, jeśli otrzymujesz błąd bazy danych (prawdopodobnie byłby to błąd 500).