ปัญหา async อาร์เรย์ว่าง nodejs พังพอน

Oct 19 2020

ฉันต้องการได้รับจากอาร์เรย์ของ ID ที่มาใน req ใครคือชื่อและราคาของแต่ละผลิตภัณฑ์และบันทึกในรูปแบบใหม่ (การชำระเงิน) สำหรับสิ่งนี้ฉันอ่านแต่ละผลิตภัณฑ์และค้นหา ID ใน 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 สำหรับฐานข้อมูลของคุณรวบรวมอาร์เรย์ของคำสัญญาจากการดำเนินการ DB ทั้งหมด แล้วใช้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)