async проблема массив пустой nodejs mongoose

Oct 19 2020

Я хочу получить из массива идентификаторов, которые входят в 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 });
        }

    });;
});

Ответы

jfriend00 Oct 20 2020 at 03:12

Вам нужно прочитать о неблокирующих асинхронных операциях в 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 });
    }
});

В этом коде отсутствуют следующие вещи:

  1. Проверка входных значений amount, clientId, products, services, officeIdи отправка соответствующих ошибок, если какие-либо из них отсутствуют или недействительны.
  2. Что делать, если указан товар, которого нет в вашей базе?
  3. Ошибка 404 может не подходить, если вы получаете ошибку базы данных (вероятно, это ошибка 500).