Search code examples
javascriptnode.jstimersettimeoutsetinterval

Get Timer ID in Node.js


I have a simple server-side timer in NodeJS application to send some data with socket to client. How do I manage to send Timer ID to client so it can be stopped from a client side?

I tried to pass returned class in socket payload, but got an error "Max call stack size exceeded.

socket.on('start', () => {
const timerID = setInterval(()=>{
   socket.emit('data',someData)
   },3000)
socket.emit('ID',timerId)
}

Also tried to map over class symbols, but nothing works.

const array = Object.getOwnPropertySymbols(timerID);

array.map((symbol) => {
clearInterval(timerID[symbol]);
 });

Found in NodeJS Documentation this:

timeout[Symbol.toPrimitive]"()

Returns: number that can be used to reference this timeout

But it also didn't work.


Solution

  • The client cannot stop your timerID and, in nodejs a timerID is not a simple integer either or something that can be stringified so it can't effectively be sent to the client.

    You could create your own integer identifier that is mapped to the actual timerID, send that to the client and then the client could send the server back a message that it wishes to cancel timer X. You would look up timerX in your Map, find the real timerID and stop the timer.

    const timerMap = new Map();
    const timerCntr = 0;
    
    socket.on('start', () => {
        const timerID = setInterval(()=>{
            socket.emit('data', someData)
        }, 3000);
    
        // create an integer reference for the timer
        // save a mapping between the integer reference and the actual timerID
        // in a server-side Map object
        const externalTimerID = timerCntr++;
        timerMap.set(externalTimerID, timerID);
        socket.emit('ID', externalTimerID);
    }
    
    // when client wants to stop the timer
    socket.on('stop', externalTimerID => {
        let timerID = timerMap.get(externalTimerID);
        clearInterval(timerID);
    
        // remove externalTimerID from the Map so we don't leak memory  
        timerMap.delete(externalTimerID);
    });
    

    Starting with node v14.9.0, you can get an integer version of the timerID that will work as a substitute for the actual timerID object.

    socket.on('start', () => {
        const timerID = setInterval(()=>{
            socket.emit('data', someData)
        }, 3000);
    
        socket.emit('ID', timerID[Symbol.toPrimitive]());
    }
    
    // when client wants to stop the timer
    socket.on('stop', externalTimerID => {
        clearInterval(externalTimerID );
    });
    

    Here's a sample nodejs app where I verified that timerID[Symbol.toPrimitive]() is a number and will work as a substitute for the timerID object.

    const timerID = setInterval(() => {
        console.log(`hi`);
    }, 500);
    
    const integerID = timerID[Symbol.toPrimitive]();
    
    console.log(typeof integerID, integerID);    // shows it is an integer 
    
    setTimeout(() => {
        console.log("clearing Interval with integerID");
        clearInterval(integerID);
    }, 2000);