You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Liquid Democracy项目While循环死循环,Vote.find未执行求助

解决Liquid Democracy委托投票功能中的死循环与异步回调问题

你的问题一眼就能看出核心症结:同步的while循环把Node.js的事件循环彻底堵死了,导致MongoDB的异步查询回调完全没有执行的机会。

为什么会这样?

Node.js是单线程的事件循环模型——同步代码会霸占线程一直跑,直到执行完毕;而异步IO操作(比如你的Vote.find)的回调会被放到事件队列里,只有当所有同步代码都跑完,事件循环才会去处理队列里的回调。

你现在的while循环里,delegationsLeft初始设为1,然后就开始无限循环:因为Vote.find的回调根本没机会执行,delegationsLeft永远不会被更新成0,循环就停不下来;同时事件队列里的回调被压着,永远进不去Vote.find的回调里,自然看不到"I'm in."的日志。

怎么修复?

我们需要把同步的while循环改成异步的循环方式,用async/await来重构代码——既保留你的逻辑,又不会阻塞事件循环,还能解决原来的回调嵌套和闭包问题。

首先,把MongoDB的回调式API改成Promise版本(mongoose默认支持返回Promise,不用传回调),然后用async/await来写异步逻辑:

async function processDelegations(propId) {
  try {
    // 获取委托投票总数
    const voteNumber = await Vote.count({ propId: propId, delegation: true });
    const maxDelegation = voteNumber;

    // 遍历每个权重等级
    for (let d = 1; d <= maxDelegation; d++) {
      let delegationsLeft;
      // 用do-await-while实现异步循环,确保每次先执行查询再判断
      do {
        console.log(`I am about to find the votes for ${d} weight, ${delegationsLeft || 1} delegation left at least.`);
        // 异步查询当前权重的委托投票
        const specWeightVotes = await Vote.find({ propId: propId, delegation: true, weight: d });
        console.log("I'm in.");
        delegationsLeft = specWeightVotes.length;

        // 逐个处理委托投票
        for (const vote of specWeightVotes) {
          try {
            // 给被委托者增加权重
            await Vote.findOneAndUpdate(
              { propId: propId, voter: vote.content },
              { $inc: { weight: vote.weight } }
            );
            // 将当前委托投票的权重设为0
            await Vote.findOneAndUpdate(
              { _id: vote._id },
              { $set: { weight: 0 } }
            );
          } catch (updateErr) {
            console.error(`处理投票${vote._id}时出错:`, updateErr);
          }
        }
      } while (delegationsLeft > 0);
    }

    console.log("I have delegated all votes");
  } catch (err) {
    console.error("处理委托流程时出错:", err);
  }
}

// 调用这个函数来处理委托
processDelegations(propId);

重构的关键点说明:

  1. 用async/await替代回调:把异步操作写成同步风格,既避免了回调地狱,又能让事件循环在await时让出线程,处理其他任务,不会阻塞。
  2. 异步循环代替同步while:do-await-while确保每次循环都会先执行异步查询,更新delegationsLeft的值,不会出现死循环。
  3. 解决闭包问题:用for...of遍历投票列表,代替原来的for(var i...)——原来的var会导致回调执行时i已经是循环结束的值,出现错误。
  4. 错误处理:添加了try/catch捕获整个流程和单个更新的错误,方便调试和问题定位。

额外提醒

原来的代码还有个隐藏问题:嵌套的findOneAndUpdate回调里,specWeightVotes[i]的i值在回调执行时已经不是当前循环的i了(因为var的作用域问题),这会导致更新错误的投票记录。用async/await和for...of就能彻底解决这个问题。

内容的提问来源于stack exchange,提问作者Adrien Merlier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:55:06