Search code examples
javascriptnode.jssocket.iocleartimeout

clearTimeout function does not run


I've made a quiz game using socket.io. When a new socket connects, this runs:

io.on('connection', function (socket) {
let gameTimer = null;

After receiving a certain socket from the client, this function starts:

gameTimer = setTimeout(function () {
//Reveal if the players' answers are right or wrong
revealAnswer(answeredPlayers, playerData[socket.id].room);

//Start the countdown for the next question
countToNextQuestion(playerData[socket.id].room);
}, 21000)

When a player answers, this socket function runs in the server:

socket.on('Answered Question', function (userAnswer, answeredPlayerUsername, timeAnswered) {
//Stop the count
clearTimeout(gameTimer);

/*
*Extra code below that works...
*/

But this does not stop the timeout. Any reason why this is happening, and how to fix it?

EDIT: Here's the full socket code. It's a bit long, apologies:

io.on('connection', function (socket) {

    console.log('Someone connected');

    //Will hold the questions used for the session
    let gameSessionQuestions = null;

    //Holds the current question
    let currentQuestion = null;

    //Holds the correct answer to the current question
    let currentQuestionAnswer = null;

    //Give the socket player details
    //and add this player to the dictionary
    playerData[socket.id] = {
        name: null,
        room: null,
        host: false,
        score: 0
    };

    //Timer for questions
    let gameTimer = null;


    /*
    SOCKET OPERATIONS
    */

    socket.on('Get Total Score', async function(playerUsername){
        let playerTotalScore = await gameController.getTotalScore(playerUsername);
        socket.emit('Receive Total Score', playerTotalScore); 
    })


    //Adds a room to the list of rooms
    socket.on('Add Room', function (questionType) {
        let newRoom = newRoomCreation(questionType);

        //Since this player created the room, assign them the host value
        playerData[socket.id].host = true;
        playerData[socket.id].room = newRoom;

        socket.join(newRoom);

        io.sockets.to(newRoom).emit('Room Added', newRoom);

    })

    //Make the specified room a closed room
    socket.on('Close the room', function (room) {
        closeRoom(room);
    })

    socket.on('Room Availability', function (roomCode, questionType) {

        if (!roomMap.get(roomCode)) {
            socket.emit('Unavailable Room');
        }
        else {
            //Check if the room has the question type the user chose
            if (roomQuestionType.get(roomCode) == questionType) {
                socket.emit('Available Room');
            } else {
                socket.emit('Unavailable Room');
            }

        }

    })

    socket.on('Open Room Availability', function (questionType) {
        let roomFound = false;

        for (let room of roomMap.keys()) {
            if (!closedRoomsList.includes(room)) {
                if (roomQuestionType.get(room) == questionType) {
                    roomFound = true;
                    socket.emit('Available Open Room', room);
                }
            }
        }

        if (roomFound == false) {
            socket.emit('Unavailable Open Room');
        }


    })

    socket.on('Join Room', function (values) {

        //Give this player's 'name' variable a value
        playerData[socket.id].name = values.p1;

        //Give this player's 'room' variable a value
        playerData[socket.id].room = values.p2;

        //DELETE SOON: Checking the data before addition
        console.log("Data in the room '" + values.p2 + "' before addition: ");
        console.log(roomMap.get(values.p2));

        //Put this player in the new room
        socket.join(values.p2);

        //Add the player's socketID and Name to the Room Map
        let playersList = roomMap.get(values.p2);
        playersList.push(values.p1);


        //DELETE SOON: Checking the data after addition
        console.log("Data in the room '" + values.p2 + "' after addition: ");
        console.log(roomMap.get(values.p2));

        io.sockets.to(values.p2).emit('Output Players', playersList);

        if (playerData[socket.id].host == true) {
            io.sockets.to(playerData[socket.id].room).emit('Current Host');
        }
    })

    socket.on('Request Game Start', async function (room) {

        //Checks if the user who started the game truly is the host
        if (playerData[socket.id].host == false) {
            return;
        }

        //Holds all the questions to be asked and their answers
        gameSessionQuestions = await selectedQuestions(2, roomQuestionType.get(room));

        //Reset the 'answered player' count
        answeredPlayers.set(playerData[socket.id].room, 0);

        io.sockets.to(room).emit('Game Start');
    })

    socket.on('Start Game Countdown', function () {

        let pos = 0;
        let countArr = ["2", "1", "Go"];

        let counter = setInterval(function () {
            //Update the count for all those in the room
            io.sockets.to(playerData[socket.id].room).emit('Update Countdown', countArr[pos]);


            if (pos == countArr.length) {
                clearInterval(counter);

                io.sockets.to(playerData[socket.id].room).emit('End Countdown');

                currentQuestion = getQuestion(gameSessionQuestions[0], playerData[socket.id].room);

                io.sockets.to(playerData[socket.id].room).emit('Receive Questions', currentQuestion);

                //Holds the answer for the current question
                currentQuestionAnswer = currentQuestionCorrectAnswer(gameSessionQuestions[0], playerData[socket.id].room);

                //The answer to the question presented in this room is stored here
                questionAnswers.set(playerData[socket.id].room, currentQuestionAnswer);

                gameTimer = setTimeout(function () {
                    //Reveal if the players' answers are right or wrong
                    revealAnswer(answeredPlayers, playerData[socket.id].room);

                    //Start the countdown for the next question
                    countToNextQuestion(playerData[socket.id].room);
                }, 21000)

            } else {
                pos++;
            }

        }, 1000);
    })

    socket.on('Answered Question', function (userAnswer, answeredPlayerUsername, timeAnswered) {

        socket.broadcast.to(playerData[socket.id].room).emit('Display Answered Player', answeredPlayerUsername);

        //DELETE SOON: Check the current question
        console.log("Current Question's Answer: " + questionAnswers.get(playerData[socket.id].room));
        console.log("Your answer: " + userAnswer);

        let answerResult;

        if (questionAnswers.get(playerData[socket.id].room) === userAnswer) {
            answerResult = "btn-success";

            //Add to the person's overall score
            playerData[socket.id].score = addToScore(playerData[socket.id].score, timeAnswered);

            socket.emit('Answer Check', answerResult);

        }
        else {
            answerResult = "btn-danger";
            socket.emit('Answer Check', answerResult);
        }

        //DELETE SOON: Checks the scores for the user
        console.log("The score for '" + playerData[socket.id].name + "' is '" + playerData[socket.id].score + "'")

        //Add 1 to the number of people who have answered in this room
        answeredPlayers.set(playerData[socket.id].room, (answeredPlayers.get(playerData[socket.id].room)) + 1)

        //DELETE SOON: Checks if the value is updating
        console.log("Number of people who have answered: " + answeredPlayers.get(playerData[socket.id].room))

        //If all users in the room have answered...
        if (answeredPlayers.get(playerData[socket.id].room) == (roomMap.get(playerData[socket.id].room)).length) {

            //DELETE SOON: Outputs message confirming all users answering
            console.log("Everyone has answered");

            //Stop the count
            clearTimeout(gameTimer);

            //Reveal if the players' answers are right or wrong
            revealAnswer(answeredPlayers, playerData[socket.id].room);

            //Start the countdown for the next question
            countToNextQuestion(playerData[socket.id].room);

        }
    })

    //Get the next question from the host
    socket.on('Find Host', async function () {

        if (playerData[socket.id].host == true) {

            //DELETE SOON: Check the number of questions before
            console.log('Question count before: ' + gameSessionQuestions.length)

            gameSessionQuestions.shift();

            //DELETE SOON: Check the number of questions after removing the one just asked
            console.log('Question count after: ' + gameSessionQuestions.length)

            //If there are still questions to be asked, send the next question and set the new correct answer
            if (gameSessionQuestions.length != 0) {

                //Get the next question and send it
                currentQuestion = getQuestion(gameSessionQuestions[0], playerData[socket.id].room);
                io.sockets.to(playerData[socket.id].room).emit('Receive Questions', currentQuestion);

                //Get this question's correct answer
                currentQuestionAnswer = currentQuestionCorrectAnswer(gameSessionQuestions[0], playerData[socket.id].room);
                questionAnswers.set(playerData[socket.id].room, currentQuestionAnswer);

                gameTimer = setTimeout(function () {
                    //Reveal if the players' answers are right or wrong
                    revealAnswer(answeredPlayers, playerData[socket.id].room);

                    //Start the countdown for the next question
                    countToNextQuestion(playerData[socket.id].room);
                }, 21000)

            } else {

                //Holds the username and scores for all users in this room
                let players_Scores = [];

                let roomCode = playerData[socket.id].room;

                let playerKeys = Object.keys(playerData);

                for (let i = 0; i < playerKeys.length; i++) {
                    if (playerData[playerKeys[i]].room == roomCode) {

                        let scoreDetails = {
                            username: playerData[playerKeys[i]].name,
                            score: playerData[playerKeys[i]].score
                        }

                        players_Scores.push(scoreDetails)

                        await gameController.uploadScore(playerData[playerKeys[i]].name, playerData[playerKeys[i]].score, roomQuestionType.get(roomCode));
                    }
                }

                io.sockets.to(roomCode).emit('Show Scores', players_Scores);

            }
        }

    })

    //Remove details while reloading the menu page
    socket.on('Back to Start', function () {

        //Remove the player from the room in socketio
        socket.leave(playerData[socket.id].room);

        //If this is the host, delete the room
        if (playerData[socket.id].host == true) {
            roomMap.delete(playerData[socket.id].room);
            roomQuestionType.delete(playerData[socket.id].room);
            questionAnswers.delete(playerData[socket.id].room);
            answeredPlayers.delete(playerData[socket.id].room);
        }

        //Remove this player's details
        delete playerData[socket.id];

    })

    socket.on('disconnect', function () {

        if (playerData[socket.id] && roomMap.get(playerData[socket.id].room) != undefined) {
            //DELETE SOON: Check who left and from which room
            console.log(playerData[socket.id].name + " has left the room '" + playerData[socket.id].room + "'");

            //If on the lobby page, remove that player and update everyone's lists
            let playerList = roomMap.get(playerData[socket.id].room);
            playerList.splice(playerList.indexOf(playerData[socket.id].name), 1);

            io.sockets.to(playerData[socket.id].room).emit('Output Players', playerList);

            /*If this is the host,
            * stop game timer
            * alert all the players in the room,
            * delete the room they were in*/
            if (playerData[socket.id].host == true) {
                clearTimeout(gameTimer);

                socket.broadcast.to(playerData[socket.id].room).emit('Host Left');
                roomMap.delete(playerData[socket.id].room);
                roomQuestionType.delete(playerData[socket.id].room);
                questionAnswers.delete(playerData[socket.id].room);
                answeredPlayers.delete(playerData[socket.id].room);
            }

            delete playerData[socket.id];
        }

    })

})

Solution

  • Your issue in the smallest code to rep

    let gameTimer;
    let pos = 0;
    let countArr = ["2", "1", "Go"];
    
    let counter = setInterval(function () {
      if (pos == countArr.length) {
        console.log("Creating a new gameTimer", Date.now());
        gameTimer = setTimeout(function () {
          console.log('gameTimer executed')
        }, 2100);
      } else {
        pos++;
      }
    }, 1000);

    So your code keeps creating new timeouts. The previous timeouts exist, they do not get removed.

    So you need to either remove the gameTimer before you create a new one, or do not create gameTimer if it is already running

    let gameTimer;
    let pos = 0;
    let countArr = ["2", "1", "Go"];
    
    let counter = setInterval(function () {
      if (pos == countArr.length) {
        if (gameTimer) return; // if gameTimer is defined, exit
        console.log("Creating a new gameTimer", Date.now());
        gameTimer = setTimeout(function () {
          console.log('gameTimer executed')
        }, 2100);
      } else {
        pos++;
      }
    }, 1000);