简体   繁体   English

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

[英]Using While loop inside async function?

I'm trying to use a while loop inside an async function, and I suspect my resolve(); 我试图在异步函数中使用while循环,并且怀疑我的resolve(); is messing up the loop, but I'm not sure what to do about it. 正在弄乱循环,但是我不确定该怎么做。 Here's the code I have: 这是我的代码:

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

The goalPerMeal variable takes a user calorie input and divides it by the number of meals they select on my form. goalPerMeal变量接受用户的卡路里输入,并将其除以他们在我的表格上选择的进餐次数。 I then set that value to a specific breakfast variable called breakfastGoal . 然后,我将该值设置为一个名为breakfastGoal的特定早餐变量。 The async function finds a random recipe from my database and adds it to my array, breakfastArr . 异步函数从我的数据库中找到一个随机配方,并将其添加到我的数组breakfastArr Once it finds the recipe, it subtracts that recipe's calorie count from the breakfastGoal . 找到食谱后,就会从breakfastGoal减去该食谱的卡路里计数。

I want this function to run until breakfastGoa l is reduced to below 150; 我希望运行此功能,直到breakfastGoa l降低到150以下; however, it doesn't seem to work. 但是,它似乎不起作用。 If I remove the while loop, the function will successfully find the breakfast item, add it to the array, and subtract its calorie count from breakfastGoal . 如果删除while循环,该函数将成功找到早餐项目,将其添加到数组中,并从breakfastGoal减去其卡路里计数。 The only thing breaking it is adding the while loop. 打破它的唯一一件事就是添加while循环。

Is this something to do with the resolve(); 这与resolve();resolve(); in the async function, or am I missing something important? 在异步功能中,还是我缺少重要的东西?

Any help is extremely appreciated. 任何帮助都非常感谢。

Do you think something like this will work? 您认为这样的事情会起作用吗? I have not tested this. 我还没有测试。 Just an idea. 只是一个主意。 put the while block inside the else block and call recursively. 将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);
          }
        });
    }
  })
}

The problem is that you create only one promise, but have several asynchronous results you need to wait for in the while loop. 问题在于,您仅创建一个Promise,但是需要在while循环中等待多个异步结果。

The first time resolve is called, that sole promise is resolved, and that will make the code following the await to execute. 第一次调用resolve ,将解决唯一的诺言,这将使代码在await执行之后。 But at that time your array is not finished at all. 但是那时您的阵列还没有完成。 Also, any further calls to resolve will not influence that promise any more: a promise can only be resolved once. 同样,任何进一步resolve呼吁将不再影响承诺:承诺只能被解决一次。 The other calls are ignored. 其他呼叫将被忽略。

The solution is to make a promise in each iteration of the while loop, and await it. 解决方案是在while循环的每次迭代中做出承诺,然后await

Change this: 更改此:

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

to this: 对此:

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

The code could be improved more, but it is not relevant to your question. 该代码可以进一步改进,但是与您的问题无关。 For instance, it would be better to make each promise resolve with the value to be added to the array breakfastArr , so that you would have: 例如,最好使用要添加到数组breakfastArr的值来使每个promise解析,这样您将具有:

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

And the async function should return that array, so that it does not have to be defined at the top. 并且async函数应该返回该数组,因此不必在顶部定义它。

The issue here is that you are mixing callback taking functions with promises. 这里的问题是,您正在混合使用回调函数和promise。 The proper way to do this is to wrap only the callback taking functions in promise constructors. 正确的方法是将仅采用回调的函数包装在promise构造函数中。 The result is some thing like this: 结果是这样的:

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

});

Some of the Promise libraries had a function called promisify that would take a function taking a callback as its last argument which took node style (err, result) arguments and produced a function that when called returned a promise. 一些Promise库具有一个称为promisify的函数,该函数将把回调作为最后一个参数的函数作为回调,该函数采用节点样式(err,result)参数,并生成一个函数,该函数在调用时返回promise。 If that is available the wrappers get a lot smaller. 如果可以的话,包装纸会变小很多。 for example the Food.count wrapper becomes let count = promisify(Food.count); 例如, Food.count包装器变为let count = promisify(Food.count); or let count = promisify(Food.count.bind(Food)); 或者let count = promisify(Food.count.bind(Food));

You are returning a promise from async function which is not required. 您从异步函数返回了一个不需要的承诺。 You can remove async from the function returning promise. 您可以从返回诺言的函数中删除异步。 So I suggest removing async from function getBreakfastArr() 所以我建议从功能getBreakfastArr()删除异步

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM