将猫鼬查询结果推送到全局数组

时间:2020-05-25 22:01:53

标签: node.js mongodb mongoose

我正在尝试使用猫鼬查询我的mongodb集合,但未呈现结果。我想我知道为什么,因为有些东西,异步/等待/承诺,但没有任何效果。基本上,我需要2个查询,其中一个要拉出所请求的链接中提到的对象(workflowID),它本身具有多个“步骤”,这些步骤在对象ID的数组中引用。它们不会被推送到范围变量(workflowStepCombined)。如果我将结果记录在forEach中,该查询将按预期工作,那么我将得到预期的结果。就像我提到的那样,我认为问题在于应用程序没有在等待结果,我已经阅读了关于Promise的猫鼬文档,并实施了我认为的解决方案,但是整天重构后却没有任何进展。

app.get("/new/:workflowID", function (req, res) {
  if (req.isAuthenticated()) {
    const workflowID = req.params.workflowID;
    //define array to push recombined steps to
    let workflowStepCombined = [];
    Workflow.findOne({ workflowName: workflowID }, function (err,foundWorkflow) {
        const stepsArray = foundWorkflow.workflowStepArray;
        //once workflow found, start pulling from step collection the listed IDs
        stepsArray.forEach(function (step) {
          Steps.findOne({ _id: step }, (err, foundStep) => {
              //push to array that was definedin first part
              workflowStepCombined.push(foundStep);
          });
        });
        //render it all together
        res.render("contracts/" + workflowID, {
          userID: req.user.username,
          workflow: workflowStepCombined,
        });
      }); 
    } else {
        res.redirect("/");
        }
    });

并且我也尝试使用.then,因为从我有限的理解出发,我需要让它等待查询结果之后再移至下一行代码。但这也不起作用,它的行为相同。

app.get("/new/:workflowID", function (req, res) {
  if (req.isAuthenticated()) {
      //allows for dynamic link creation
    const workflowID = req.params.workflowID;
    const stepsArray = [];
    //define array to push recombined steps to
    let workflowStepCombined = [];

    //find workflow with name from link
    Workflow.findOne({ workflowName: workflowID }).then(foundWorkflow => {
      stepsArray.push(foundWorkflow.workflowStepArray);
    })
    console.log(stepsArray)
    //once workflow found, start pulling from step collection the listed IDs
    stepsArray.forEach(function (step) {
    Steps.findOne({ _id: step }).then(foundStep => {
        workflowStepCombined.push(foundStep);
        console.log(foundStep);
     });
    });
      //render it all together
      res.render("contracts/" + workflowID, {
        userID: req.user.username,
        workflow: workflowStepCombined,
      });

2 个答案:

答案 0 :(得分:0)

forEach函数不是异步的,因此您在获取步骤结果之前调用res.render。您应该使用Promise.all函数

Promise.all(stepsArray.map(step => Steps.findOne({ _id: step })))
  .then(workflowStepCombined => {
    return  res.render("contracts/" + workflowID, {
      userID: req.user.username,
      workflow: workflowStepCombined,
    });
  })

我建议您使用异步/等待方法,而不要使用回调

app.get("/new/:workflowID", async (req, res) => {
  if (req.isAuthenticated()) {
    const workflowID = req.params.workflowID;
    //define array to push recombined steps to
    let workflowStepCombined = [];
    const workflow = await Workflow.findOne({ workflowName: workflowID });
    const stepsArray = foundWorkflow.workflowStepArray;
    const workflowSteps = await Steps.find({_id: {$in: stepsArray}}) //https://docs.mongodb.com/manual/reference/operator/query/in/ and you could use sort here if needed - https://mongoosejs.com/docs/api.html#model_Model.find

    //render it all together
    res.render("contracts/" + workflowID, {
      userID: req.user.username,
      workflow: workflowSteps,
    });
  } else {
    res.redirect("/");
  }
});

答案 1 :(得分:0)

是的,您说对了,问题出在服务器发送响应之前,诺言没有得到解决。这部分代码是罪魁祸首:

stepsArray.forEach(function (step) {
  Steps.findOne({ _id: step }, (err, foundStep) => {
      //push to array that was definedin first part
      workflowStepCombined.push(foundStep);
  });
});

Array.forEach在异步回调方面比较棘手,在您的情况下,您希望每个循环在下一个循环之前执行并解决,以便在循环结束时,所有findOne查询的结果将位于workflowStepCombined数组中,但是情况并非如此,您可以签出this article for why

直接的解决方法是像这样使用Promise.all

Promise.all(stepsArray.map(stepId => Steps.findOne({_id: stepId})))
  .then(workflowStepCombined => {
    res.render("contracts/" + workflowID, {
      userID: req.user.username,
      workflow: workflowStepCombined,
    });
  })

但是,由于sepsArray是工作流程步骤_id的数组,因此您可以使用单个查找查询,而不必循环执行多个查询。像这样:

Steps.find({ _id: { $in: stepsArray } })

这应该返回包含stepsArray中标识的所有步骤对象的数组,因此不需要循环和推送。将此与上一个查询结合起来,端点应该是这样的:

app.get("/new/:workflowID", function (req, res) {
  if (req.isAuthenticated()) {
    const workflowID = req.params.workflowID;

    Workflow.findOne({ workflowName: workflowID }, function (err,foundWorkflow) {
        const stepsArray = foundWorkflow.workflowStepArray;

        Steps.find({ _id: { $in: stepsArray } }, function(error, workflowStepCombined) {

          res.render("contracts/" + workflowID, {
            userID: req.user.username,
            workflow: workflowStepCombined,
          });

        })
      }); 
    } else {
        res.redirect("/");
        }
    });

记住要捕捉并处理可能的错误