【问题标题】:How can I make a timer on the backend that is unique to every user which can be cleared using socketio events如何在后端创建一个计时器,该计时器对每个用户都是唯一的,可以使用 socketio 事件清除
【发布时间】:2025-12-06 21:10:01
【问题描述】:

我正在使用 MERN 堆栈和 SocketIO 制作一个基于回合的多人游戏。我希望每个玩家在游戏将他们踢出局之前有 60 秒的时间轮到他们上场。一旦该玩家播放,我希望取消计时器。我尝试为游戏中的每个玩家创建一个 setTimeout 函数,然后使用 clearTimeout 根据 socketIO 事件取消计时器,但实际上并没有取消计时器。这可能是一个实现问题,所以我想知道实现此功能的最佳方法是什么?这是我当前的实现:

socket.on('Start Countdown', () => {
    let user = getUser(socket.id);
    user.countDown = setTimeout(() => {
      socket.emit('Error', { error: { msg: 'You took too long' } });
      socket.emit('AFK');
    }, 60000);
  });
  socket.on('Stop Countdown', () => {
    let user = getUser(socket.id);
    if (user.countDown) {
      clearTimeout(user.countDown);
      console.log(user.countDown);
    }
    console.log(user.countDown);
  });

如前所述,当“停止倒计时”发生时,计时器不会被取消。 注意:getUser 根据提供的 socket.id 从 users 数组返回一个 js 对象 user,形式为 {socketId, userId, roomId, countDown}。该数组中不会有两个元素具有相同的 userId,并且在刷新时,socketId 元素将更新为正确的 socket.id。因此,您可以假设 getUser 每次都会返回相同的用户对象,即使在刷新之后也是如此。

【问题讨论】:

    标签: javascript node.js socket.io settimeout backend


    【解决方案1】:

    猜猜应该可以,但是您可以尝试定义一个新的空对象,其 ID 是 client.id,因此您可以为其分配 setTimeout 值,从那里您可以检查是否有一个计时器已经为该用户运行或者最后取消。

    const timers = {};
    
    socket.on('Start Countdown', () => {
        if (typeof timers[socket.id] !== 'undefined') {
            clearTimeout(timers[socket.id]);
        }
    
        timers[socket.id] = setTimeout(() => {
            socket.emit('Error', { error: { msg: 'You took too long' } });
            socket.emit('AFK');
        }, 60000);
    });
    
    socket.on('Stop Countdown', () => {
        if (typeof timers[socket.id] !== 'undefined') {
            clearTimeout(timers[socket.id]);
            delete timers[socket.id];
        }
    });
    

    希望对您有所帮助! ?

    【讨论】:

    • 我看到的问题是,每当用户刷新时,都会给出一个新的 socket.id 并启动一个新的计时器。这意味着用户可以不断刷新并获得一个新的计时器。我创建的 getUser 函数返回一个特定的 js 对象,其中包含 userid、roomid、countdown 函数和 socket.id。每当用户刷新时,该特定对象的 socket.id 都会更改,以便稍后可以使用 socket.id 找到它。我试过这样做,但没有奏效。
    • 这是一个简短的示例,您可以从中创建一个类,并使用该类管理实际用户及其计时器。 ?
    【解决方案2】:

    我不知道getUser 是如何工作的,但是如果从数据库中获取user var 在Start Countdown 事件结束时被销毁,您可以尝试定义一个对象并分配用户的一些唯一属性比如id或者username作为key,timeout作为value:

    
    var timeouts = {} //this must go outside the socket connection event
    
    socket.on('Start Countdown', () => {
        let user = getUser(socket.id);
        //it could be user.id, user.username or some property of user that is unique and unvariable
        timeouts[user.userId] = setTimeout(() => {
          socket.emit('Error', { error: { msg: 'You took too long' } });
          socket.emit('AFK');
        }, 60000);
      });
      socket.on('Stop Countdown', () => {
        let user = getUser(socket.id);
        if (timeouts[user.userId]) {
          clearTimeout(timeouts[user.userId]);
          console.log(timeouts[user.userId]);
        }
        console.log(timeouts[user.userId]);
      });
    

    【讨论】:

    • 我之所以使用getUser是因为如果用户刷新,会有一个新的socket.id。无论 socket.id 是什么,getUser(socket.id) 都会返回一个 js 对象,其中包含用户的正确 mongodb objectId、roomId 和倒计时函数。根据我从您的代码中了解到的情况,每当用户刷新时,套接字都会获得一个新的 socket.id 并因此开始一个新的倒计时,但我不希望这种情况发生,因为这样用户就可以继续刷新并重新开始倒计时.让我知道我的解释是否错误以及是否有解决此问题的方法。
    • 你是对的,socket.id 在这种情况下不应该是关键。所以你可以继续使用 getUser(socket.id) 但user 中必须有一些独特的东西可以用作密钥。我会用代码编辑答案
    最近更新 更多