Сохранение возвращаемого значения node.js setTimeout в redis

Я использую setTimeout в Node.js, и, похоже, он отличается от клиентского setTimeout тем, что возвращает объект вместо числа. Я хочу сохранить это в redis, но поскольку redis хранит только строки, мне нужно преобразовать объект в строку. Однако, используя JSON.stringify, выдается круговая контрольная ошибка. Как сохранить этот объект в redis, если я хочу получить его из redis и вызвать clearTimeout на нем?

Ответ 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);

Тайм-ауты сами по себе не сохраняются на перезапуске сервера, но вы можете решить это, отпустив метод подметания при запуске. Подключенные клиенты вернутся "в сети" довольно быстро.