首页 > 解决方案 > 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.

标签: javascriptnode.jstimersettimeoutsetinterval

解决方案


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);

推荐阅读