猫鼬异步找到所有并为每个更新

时间:2019-06-08 21:45:47

标签: javascript node.js asynchronous mongoose async-await

我没有小费游戏。游戏结束后,我会从猫鼬数据库中获得所有提示,然后使用forEach遍历这些提示。

对于每一个提示,我都获得了用户名并从mongoose db加载用户以增加该用户的积分,然后保存该用户,将其更改回db。

一个用户可以拥有多个提示。

Tips.find({...}).exec(function(err, gameTips) {

   gameTips.forEach(tip, i) => {

      User.findOne({
         username: tip.username
      }).exec(function(err, user) {

          user.points = user.points + 1;

          user.save(function(err) {
             console.log("Points were increased");
          });
      });
   });
}

现在,我的问题是在保存上一个技巧之前,完成了用户的findOne操作。因此,积分将无法正确增加。

用户:testUser有4个提示| 预期:testUser.points = 4; | 当前:testUser.points = 2;

是否可以异步执行此操作,以便为所有用户进行查找和保存操作一次又一次,以便每次执行此操作:

user.points = user.points +1;

我会在增加积分之前获得更新的积分吗?


编辑

感谢您的提示。我试图采用这种方法,现在我的代码是:

async function updateUserPoints(schedule) {
try {
    console.log("Load Schedules");
    const scheduleTips = await Tip.find({
        scheduleId: schedule._id,
        season: schedule.season
    });

    console.log(scheduleTips);

    if (scheduleTips.length) {

        for (const scheduleTip of scheduleTips) {
            console.log("Load User for scheduleTip: " + scheduleTip.tip);
            let user = await User.findOne({
                username: scheduleTip.username
            })

            console.log(user);
            if (user) {
                const winner = calculateWinner(schedule);
                const points = calculatePoints(scheduleTip, winner);

                console.log("WINNER: " + winner);
                console.log("POINTS: " + points);

                user.tippspiel.overallPoints = user.tippspiel.overallPoints + points;
                user.tippspiel.seasonPoints = user.tippspiel.seasonPoints + points;
                user.tippspiel.gameWeekPoints = user.tippspiel.gameWeekPoints + points;

                await user.update({ username: scheduleTip.username }, { $inc: { "tippspiel.overallPoints": points } }, function(err) {
                    if (err) {
                        logger.error("[Tippspiel-User]: " + err);
                    } else {
                        logger.info("[Tippspiel-User]: User tippspiel points were updated.");
                    }
                });
            }
        }
    }

} catch (err) {
    console.error(err);
}
}

function calculateWinner(schedule) {
let winner;

if (schedule.homeScore > schedule.awayScore) {
    //Home wins
    winner = "home";
} else if (schedule.homeScore < schedule.awayScore) {
    //Away wins
    winner = "away";
} else if (schedule.homeScore == schedule.awayScore) {
    //Tie/Draw
    winner = "draw";
}
return winner;

}

function calculatePoints(scheduleTip, winner) {
const POINTS_CORRECT_WINNER = settings.tippspiel.pointsCorrectWinner;
const POINTS_CORRECT_DRAW = settings.tippspiel.pointsCorrectDraw;
//If user has tipped correct
if (scheduleTip.tip === winner) {
    let points = 0;

    if ((scheduleTip.tip === "home") || (scheduleTip.tip === "away")) {
        points = points + POINTS_CORRECT_WINNER;

    } else if (scheduleTip.tip === "draw") {
        points = points + POINTS_CORRECT_DRAW;
    }

    return points;
} else {
    return 0;
}
}

我现在将对其进行测试:)

2 个答案:

答案 0 :(得分:1)

您不能像在forEach中那样使用异步代码,它不会产生预期的结果。您可以将for ofasync await结合使用,以获得更简洁的代码:

 async function updateTips() {
 try {
    const tips = await Tips.find({condition: 'condition'})
    if (tips.length) { // check for empty result
        for (const tip of tips) {
            let user = await User.findOne({ username: tip.username })
            if (user) {
                user.points = user.points + 1
                await user.save()
                console.log('Points were increased')
            }
        }
    }
 } catch (err) {
     // handle errors here
 }
}

updateTips()

答案 1 :(得分:1)

发生的事情是,您使用先前的点来计算下一个分数,而不是使用mongoDB $inc运算符

使用回调的方法1,丑陋且根本不可读

Tips.find({})
  .exec(function(err, gameTips) {
    if(err) {
      console.error(err);
      return;
    }
    gameTips.forEach(tip => {
      User.findOneAndUpdate(
        { username: tip.username },
        { $inc: { points: tip.points }}
      ).exec(function(err, user) {
        if(err) {
          console.error(err);
          return;
        }
        console.log("Points were increased");
      })
    })
  })

使用Promises的选项2,很多使用Promise.all()更具可读性

Tips.find({})
  .then(gameTips => Promise.all(gameTips.map(tip => User.updateOne(
    { username: tip.username},
    { $inc: { points: tip.points } }
  )))
  .then(() => {
    console.log("Points were increased");
  })
  .catch(console.error)

选项3使用async / await,我最喜欢的,简单易读的

async function run() {
  try {
    const gameTips = await Tips.find({});
    await Promise.all(gameTips.map(tip => User.updateOne(
      { username: tip.username},
      { $inc: { points: tip.points } }
    )));
    console.log("Points were increased");
  } catch (err) {
    console.error(err);
  }
}