在异步函数中使用While循环?

时间:2018-12-25 20:50:27

标签: javascript node.js mongodb async-await

我正在尝试在异步函数中使用while循环,并且怀疑我的resolve();正在弄乱循环,但是我不确定该怎么做。这是我的代码:

app.get("/meal-plan", async function(req, res) {

    var calorieCount = req.query.calorieCount;
    var mealInput = parseInt(req.query.mealInput);
    var dietInput = req.query.food;
    var goalPerMeal = (calorieCount / mealInput);
    var whichDiet = "diet." + dietInput;

    var breakfastGoal = goalPerMeal;

    var breakfastArr = [];

    async function getBreakfastArr(){
            return new Promise((resolve, reject) => {
                var breakfastQuery = {"breakfast": true, [whichDiet]: true};
                while (breakfastGoal >= 150) {
                    Food.count(breakfastQuery, function(err, count) {
                    if (err) {
                        console.log(err);
                    } else {
                        var random = Math.floor(Math.random() * count);
                        Food.findOne(breakfastQuery).skip(random).exec(
                            function(err, result) {
                                if (err) {
                                    console.log(err);
                                } else {
                                    breakfastGoal -= result.nutrition.calories;
                                    breakfastArr.push(result);
                                    resolve();
                                }
                          });
                     }
                 })
            }
        });
    }


    try {

        await getBreakfastArr();
        console.log(breakfastArr);

        res.render("meal-plan.ejs", { meal: mealInput, calories: calorieCount, diet: dietInput, breakfast: breakfast, lunch: lunch, dinner: dinner, totalCalories: totalCalories});

    } catch (e){
        res.json(e);
    }

});    

goalPerMeal变量获取用户的卡路里输入,并将其除以他们在我的表单上选择的进餐次数。然后,我将该值设置为一个特定的早餐变量,称为breakfastGoal。 Async函数从我的数据库中找到一个随机配方,并将其添加到我的数组breakfastArr中。找到食谱后,它将从BreakfastGoal中减去该食谱的卡路里计数。

我希望此功能一直运行到breakfastGoal减少到150以下;但是,它似乎不起作用。如果删除While循环,该函数将成功找到早餐项目,将其添加到数组中,并从breakfastGoal中减去其卡路里计数。打破它的唯一一件事就是添加While循环。

这与resolve()有关吗?在异步功能中,还是我缺少重要的东西?

非常感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

您认为这样的事情行得通吗?我还没有测试。只是一个主意。 将while块放入else块中,然后递归调用。

async function getBreakfastArr() {
  let breakfastQuery = { "breakfast": true, [whichDiet]: true };
  return this.getRandomRecipe(breakfastQuery).then((response)=>{
        return response; //the response will have your breakfast goal
  });
}


async function getRandomRecipe(breakfastQuery) {

   return Food.count(breakfastQuery, function (err, count) {
    if (err) {
      console.log(err);
    } else {
      var random = Math.floor(Math.random() * count);
      Food.findOne(breakfastQuery).skip(random).exec(
        function (err, result) {
          if (err) {
            console.log(err);
          } else {
            breakfastGoal -= result.nutrition.calories;
            breakfastArr.push(result);
            while (breakfastGoal >= 150) {
               this.getRandomRecipe(breakfastQuery);
            }
            return Promise.resolve(breakfastGoal);
          }
        });
    }
  })
}

答案 1 :(得分:0)

问题是您只创建一个诺言,但是在while循环中需要等待几个异步结果。

第一次调用resolve时,唯一的承诺即被解决,这将使await之后的代码得以执行。但是那时您的阵列还没有完成。另外,对resolve的任何进一步调用将不再影响该承诺:一个承诺只能被解析一次。其他呼叫将被忽略。

解决方案是在while循环的每个迭代中做出承诺,并await进行迭代。

更改此:

    return new Promise((resolve, reject) => {
        var breakfastQuery = {"breakfast": true, [whichDiet]: true};
        while (breakfastGoal >= 150) {

对此:

    var breakfastQuery = {"breakfast": true, [whichDiet]: true};
    while (breakfastGoal >= 150) {
        await new Promise((resolve, reject) => {

可以进一步改善代码,但与您的问题无关。例如,最好使用要添加到数组breakfastArr的值来使每个promise解析,这样您将拥有:

        breakfastArr.push(await new Promise((resolve, reject) => {
             // ...
                           resolve(result)
             // ...
        });

async函数应返回该数组,因此不必在顶部定义它。

答案 2 :(得分:0)

这里的问题是,您正在混合使用回调函数和promise。正确的方法是将仅采用回调的函数包装在promise构造函数中。结果是这样的:

app.get("/meal-plan", async function(req, res) {

  var calorieCount = req.query.calorieCount;
  var mealInput = parseInt(req.query.mealInput);
  var dietInput = req.query.food;
  var goalPerMeal = (calorieCount / mealInput);
  var whichDiet = "diet." + dietInput;

  var breakfastGoal = goalPerMeal;

  function count(query) {
    Food.count(query, function(err, result) {
      if (err) {
        reject(error)
      } else {
        resolve(result)
      }
    });
  }

  function findOne(query, count) {
    return new Promise((resolve, reject) => {
      Food.findOne(query).skip(count).exec(function(err, result) {
        if (err) {
          reject(error)
        } else {
          resolve(result)
        }
      });
    });
  }

  async function getBreakfastArr() {
    let breakfastQuery = {
      "breakfast": true,
      [whichDiet]: true
    };
    let breakfastArr = [];
    while (breakfastGoal >= 150) {
      let count = await count(breakfastQuery);
      let random = Math.floor(Math.random() * count);
      let result = await findOne(breakfastQuery, random);
      breakfastGoal -= result.nutrition.calories;
      breakfastArr.push(result);
    }
    return breakfastArr
  }

  try {
    let breakfastArr = await getBreakfastArr();
    console.log(breakfastArr);

    res.render("meal-plan.ejs", {
      meal: mealInput,
      calories: calorieCount,
      diet: dietInput,
      breakfast: breakfast,
      lunch: lunch,
      dinner: dinner,
      totalCalories: totalCalories
    });

  } catch (e) {
    res.json(e);
  }

});

某些Promise库具有一个名为promisify的函数,该函数将接受以回调作为最后一个参数的函数,该函数接受节点样式(err,result)参数,并生成一个函数,该函数在被调用时返回一个promise。如果可以的话,包装纸会变小很多。例如Food.count包装器变成let count = promisify(Food.count);let count = promisify(Food.count.bind(Food));

答案 3 :(得分:-3)

您从异步函数返回了一个不需要的承诺。您可以从返回诺言的函数中删除异步。 所以我建议从功能getBreakfastArr()中删除异步