for循环中使用setTimeout更新MongoDB数据时,Bee角色能量耗尽后无法恢复状态的问题修复咨询
问题分析与修复方案
你遇到的问题主要来自两个核心点:循环中的闭包变量陷阱和数据库更新未持久化,咱们一步步拆解解决:
1. 循环闭包导致的变量引用错误
你的for循环里,变量a是在循环外部作用域声明的。当5秒后setTimeout的回调执行时,循环早就跑完了,a的值会变成en.length + 1,这时候data.Hives[Hive${a}]根本不存在,自然没法修改属性。
解决这个问题最直观的方式是改用forEach遍历,每个回调函数都有独立的作用域,能正确捕获当前的 hive 键值。
2. 数据库未持久化更新
你在setTimeout里只修改了内存中的data对象,但没有把这些修改保存到数据库。而且原来的Hive.findOneAndUpdate(user, data)是在循环结束后立刻执行的,这时候setTimeout的回调还没触发,后续的属性恢复操作完全不会被持久化。
正确的做法是在setTimeout回调里,重新查询最新的数据库数据,修改后再调用save()完成持久化。
修复后的完整代码
// 上方无关代码 Hive.findOne({ User: message.author.id }, async (err, data) => { // 此处无关代码 const arr = []; // 改用forEach遍历,规避闭包变量问题 Object.keys(data.Hives).forEach(async (key) => { const hive = data.Hives[key]; if (!isNaN(hive.Energy)) { hive.Energy -= 1; if (hive.Energy < 1) { arr.push(`😴 Your ${hive.Bee} is out of energy! It's going to sleep.`); hive.Gathers = 0; hive.Converts = 0; setTimeout(async () => { // 重新查询数据库,确保拿到最新数据 const latestHiveData = await Hive.findOne({ User: message.author.id }); if (!latestHiveData) return; const targetHive = latestHiveData.Hives[key]; if (!targetHive) return; // 恢复Bee的所有属性 targetHive.Energy = targetHive.MaxEnergy; targetHive.Gathers = 2.5 * (1 + 0.1 * (targetHive.Level - 1)); targetHive.Converts = (80 * (1 + 0.1 * (2 - 1)) * statsProfile.ConvertRate) / 100; // 将修改持久化到数据库 await latestHiveData.save(); }, 5000); } } }); if (arr.length !== 0) { message.reply({ embeds: [ new Discord.MessageEmbed() .setTitle("Out of energy...") .setDescription(arr.join(`\n`)) .setColor("ffbe42"), ], }); } // 先保存当前的能量扣除等修改 await data.save(); });
关键修改说明
- forEach遍历替代for循环:每个回调拥有独立作用域,确保
setTimeout能正确引用到对应的 hive 数据。 - setTimeout内重新查询数据:避免因其他并发操作导致的脏数据问题,保证修改的是最新的数据库记录。
- 在回调内调用save():把属性恢复的操作真正写入数据库,而不是只修改内存中的临时对象。
- 提前保存当前修改:循环中的能量扣除等操作,需要先持久化到数据库,避免丢失。
这样修改后,当Bee能量归0时,5秒后就能正确恢复所有属性了。
内容的提问来源于stack exchange,提问作者PG3DGAMING
相关产品推荐
相关产品推荐

