Я использую setTimeout
в Node.js, и, похоже, он отличается от клиентского setTimeout
тем, что возвращает объект вместо числа. Я хочу сохранить это в redis, но поскольку redis хранит только строки, мне нужно преобразовать объект в строку. Однако, используя JSON.stringify
, выдается круговая контрольная ошибка. Как сохранить этот объект в redis, если я хочу получить его из redis и вызвать clearTimeout
на нем?
Сохранение возвращаемого значения node.js setTimeout в redis
Ответ 1
Вы не можете сохранить объект в 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]);
Ответ 2
Этот код используется, когда таймауты не должны быть постоянными во время перезагрузки сервера.
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();
});
});
Если вам нужно, чтобы тайм-ауты были постоянными во время перезагрузки сервера, вам может потребоваться сохранить значения _idleStart
и _idleTimeout
для каждого таймера в redis и загружать их каждый раз, когда сервер перезагружается
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 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)
});
});
Ответ 3
Я пытался сделать то же самое, что и 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);
Тайм-ауты сами по себе не сохраняются на перезапуске сервера, но вы можете решить это, отпустив метод подметания при запуске. Подключенные клиенты вернутся "в сети" довольно быстро.