Используя цикл while внутри асинхронной функции? - PullRequest
0 голосов
/ 25 декабря 2018

Я пытаюсь использовать цикл while внутри асинхронной функции, и я подозреваю, что мой resol ();портит цикл, но я не уверен, что с этим делать.Вот код, который у меня есть:

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 принимает данные о потреблении калорий пользователя и делит их на количество блюд, которые они выбирают в моей форме.Затем я устанавливаю это значение для определенной переменной для завтрака под названием завтрак.Функция Async находит случайный рецепт из моей базы данных и добавляет его в мой массив, завтракArr.Как только он находит рецепт, он вычитает количество калорий этого рецепта из завтрака.

Я хочу, чтобы эта функция работала до тех пор, пока завтрак не будет понижен до 150;однако, это не похоже на работу.Если я удалю цикл «В то время как», функция успешно найдет элемент завтрака, добавит его в массив и вычтет его количество калорий из функции завтрак-завтрак.Единственное, что его нарушает - это добавление цикла While

Это как-то связано с resol ();в асинхронной функции, или я упускаю что-то важное?

Любая помощь очень ценится.

Ответы [ 4 ]

0 голосов
/ 26 декабря 2018

Проблема в том, что вы смешиваете функции обратного вызова с обещаниями.Правильный способ сделать это - обернуть только функции обратного вызова в конструкторы обещаний.В результате получается что-то вроде этого:

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) ипроизвел функцию, которая при вызове вернула обещание.Если это доступно, обертки становятся намного меньше.например оболочка Food.count становится let count = promisify(Food.count); или let count = promisify(Food.count.bind(Food));

0 голосов
/ 26 декабря 2018

Как вы думаете, что-то подобное будет работать?Я не проверял это.Просто идея.поместите блок 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);
          }
        });
    }
  })
}
0 голосов
/ 26 декабря 2018

Проблема в том, что вы создаете только одно обещание, но у вас есть несколько асинхронных результатов, которые вам нужно ждать в цикле while.

При первом вызове resolve это единственное обещание разрешается, и после этого выполняется код, следующий за await.Но в то время ваш массив еще не закончен.Кроме того, любые дальнейшие вызовы resolve больше не будут влиять на это обещание: обещание может быть выполнено только один раз.Другие звонки игнорируются.

Решение состоит в том, чтобы давать обещание на каждой итерации цикла while и await it.

Изменить это:

    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, чтобы у вас было:

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

А функция async должна вернуть этот массив, чтобы его не нужно было определять сверху.

0 голосов
/ 26 декабря 2018

Вы возвращаете обещание из асинхронной функции, которая не требуется.Вы можете удалить async из функции, возвращающей обещание.Поэтому я предлагаю удалить асинхронную функцию getBreakfastArr ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...