我setTimeout
在 Node.js 中使用,它的行为似乎与客户端不同setTimeout
,因为它返回一个对象而不是数字。我想把这个存储在redis中,但是由于redis只存储字符串,所以我需要将对象转换为字符串。但是,使用JSON.stringify
会引发循环引用错误。如果我希望能够从 redis 中获取并调用clearTimeout
它,如何将这个对象存储在 redis 中?
4 回答
您不能将对象存储在 Redis 中。该setTimeout
方法返回一个处理程序(对象引用)。
一种想法是在内存中创建自己的关联数组,并将索引存储在 Redis 中。例如:
var nextTimerIndex = 0;
var timerMap = {};
var timer = setTimeout(function(timerIndex) {
console.log('Ding!');
// Free timer reference!
delete timerMap[timerIndex];
}, 5 * 1000, nextTimerIndex);
// Store index in Redis...
// Then, store the timer object for later reference
timerMap[nextTimerIndex++] = timer;
// ...
// To clear the timeout
clearTimeout(timerMap[myTimerIndex]);
我试图做和 OP 一样的事情。我的解决方案是在断开连接处理程序中对超时内的新键进行条件检查来设置超时:
redis.hset("userDisconnecting:" + userId, "disconnect", 1);
setTimeout(function() {
redis.hget("userDisconnecting:" + userId, "disconnect",
function(err, result) {
if (result.toString() === "1") {
//do stuff, like notify other clients of the disconnect.
}
});
}, 10000);
然后,当客户端再次连接时,我将该键设置为0
,因此在真正断开连接时需要触发的东西不会发生:
redis.hset("userDisconnecting:" + userId, "disconnect", 0);
超时本身在服务器重新启动时不会持续存在,但您可以通过在启动时启动清扫器方法来解决这个问题。连接的客户很快就会“在线”回来。
在较新版本的 node 中,您可以使用对象的 IdTimeout
而不是对象本身来结束循环。
redisClient.set('time', JSON.stringify(10))
let timeoutObject = setInterval(async function(){
let time = await JSON.parse(redisClient.get('time'))
if(time === 0){
let intervalId = await JSON.parse(redisClient.get('intervalId'))
clearInterval(intervalId)
}
time -= 1
redisClient.set('time', JSON.stringify(time))
}, 1000)
let intervalId = timeoutObject[Symbol.toPrimitive]()
redisClient.set('intervalId', JSON.stringify(intervalId))
这只是一个使用setInterval
和redis
组合构建的计时器的示例。如您所见,您可以获取 Timeout 对象的 Id 并将其存储以结束 setInterval 的执行,而不是尝试存储整个对象。
这是节点文档的链接:https ://nodejs.org/api/timers.html#timers_timeout_symbol_toprimitive
当超时不需要在服务器重新启动时保持不变时使用此代码
var timeouts = {};
app.get('/', function (req, res) {
var index = timeouts.length;
timeouts[index] = setTimeout(console.log, 1000000, req.user.name);
redis.set('timeout:' + req.user.name, index, function (err, reply) {
res.end();
});
});
app.get('/clear', function (req, res) {
redis.get('timeout:' + req.user.name, function (err, index) {
clearTimeout(timeouts[index]);
delete timeouts[index];
redis.delete('timeout:' + req.user.name);
res.end();
});
});
如果您需要在服务器重新启动时保持超时,那么您可能需要为 redis 中的每个计时器存储_idleStart
和_idleTimeout
值,并在每次服务器重新启动时加载它们
app.get('/', function (req, res) {
var timeout = setTimeout(console.log, 1000000, req.user.name);
var time = timeout._idleStart.getTime() + timeout._idleTimeout;
redis.set('timeout:' + req.user.name, time, function (err, reply) {
res.end();
});
});
app.get('/clear', function (req, res) {
redis.delete('timeout:' + req.user.name);
res.end();
});
// Load timeouts on server start
// *I know this is not the correct redis command*
// *It's not accurate, only approx*
redis.get('timeout:*', function (err, vals) {
vals.forEach(function (val) {
var time = val - new Date().getTime();
setTimeout(console.log, time, username)
});
});