array problema asincrono vuoto nodejs mangusta

Oct 19 2020

Voglio ottenere da una serie di ID presenti nel req. body il nome e il prezzo di ciascuno dei prodotti e salvarlo in un nuovo modello (pagamento), per questo esamino ogni prodotto e cerco il suo ID nel db (Product.findById) e salvo come oggetto in arrayProducts ciò di cui ho bisogno (nome e prezzo), ma il newPayment viene creato prima che arrayProducts abbia finito di riempirsi e lo creo vuoto, come potrei fare in modo che venga creato il newPayment dopo la fine di ogni prodotto?

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 });
        }

    });;
});

Risposte

jfriend00 Oct 20 2020 at 03:12

È necessario leggere alcune informazioni sulle operazioni asincrone e non bloccanti in node.js. Il tuo Product.findById()è non bloccante e asincrono, il che significa che il suo callback viene chiamato LONG dopo che l'intero gestore di route ha terminato l'esecuzione e sicuramente dopo console.log(arrayProducts);e tutto il codice che viene dopo.

Dal momento che vuoi eseguire N richieste dal tuo database e vuoi solo sapere quando sono tutte completate, il modo più efficiente per farlo sarebbe usare l'interfaccia Promise per il tuo database, raccogliere una serie di promesse da tutte le operazioni del DB e poi utilizzare Promise.all()per informare quando hanno finito.

Questo è quello che suggerirei:

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 });
    }
});

Altre cose che mancano da questo codice sono:

  1. Convalida dei valori di input per amount, clientId, products, services, officeIde invio di errori appropriati se qualcuno di essi manca o non è valido.
  2. Cosa fare se viene specificato un prodotto che non si trova nel database?
  3. Un errore 404 potrebbe non essere appropriato se ricevi un errore del database (che probabilmente sarebbe un errore 500).