async problem tablica pusta nodejs mongoose

Oct 19 2020

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

jfriend00 Oct 20 2020 at 03:12

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:

  1. 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.
  2. Co zrobić, jeśli określono produkt, którego nie ma w Twojej bazie danych?
  3. Błąd 404 może nie być odpowiedni, jeśli otrzymujesz błąd bazy danych (prawdopodobnie byłby to błąd 500).