async problema matriz vacía nodejs mangosta

Oct 19 2020

quiero obtener de un arreglo de IDs que vienen en el cuerpo requerido el nombre y precio de cada uno de los productos y guardarlo en un nuevo modelo (pago), para esto reviso cada producto y busco su id en el db (Product.findById) y guardo como un objeto en arrayProducts lo que necesito (nombre y precio), pero el newPayment se crea antes de que el arrayProducts termine de llenarse y lo creo vacío, como podría hacer para que se cree el newPayment después de la finalización de cada uno de los productos?

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

    });;
});

Respuestas

jfriend00 Oct 20 2020 at 03:12

Debe leer un poco sobre operaciones asincrónicas sin bloqueo en node.js. Your Product.findById()es no bloqueante y asincrónico, lo que significa que su devolución de llamada se llama LONG después de que todo el controlador de ruta haya terminado de ejecutarse y, ciertamente, después de console.log(arrayProducts);y todo el código que viene después de eso.

Dado que desea realizar N solicitudes de su base de datos y solo desea saber cuándo están todas hechas, la forma más eficiente de hacerlo sería usar la interfaz Promise para su base de datos, recopilar una matriz de promesas de todas las operaciones de la base de datos y luego usar Promise.all()para informar cuando estén todos hechos.

Esto es lo que sugeriría:

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

Otras cosas que faltan en este código son:

  1. Validar los valores de entrada amount, clientId, products, services, officeIdy enviar los errores correspondientes si alguno de ellos falta o no es válido.
  2. ¿Qué hacer si se especifica un producto que no se encuentra en su base de datos?
  3. Un error 404 puede no ser apropiado si recibe un error de la base de datos (que probablemente sería un error 500).