Използвам setTimeout
в Node.js и изглежда, че се държи различно от setTimeout
от страна на клиента, тъй като връща обект вместо число. Искам да съхраня това в redis, но тъй като redis съхранява само низове, трябва да конвертирам обекта в низ. Въпреки това, използването на JSON.stringify
хвърля кръгова референтна грешка. Как мога да съхраня този обект в redis, ако искам да мога да го изтегля от redis и да извикам clearTimeout
върху него?
Съхраняване на върнатата стойност на node.js setTimeout в redis
Отговори (4)
Не можете да съхранявате обекта в Redis. Методът setTimeout
връща Handler (препратка към обект).
Една идея би била да създадете свой собствен асоциативен масив в паметта и да съхраните индекса в 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]);
clearTimeout
на обекта, след като го изтегля от redis
- person user730569; 03.07.2012
Опитвах се да направя същото като 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);
Самите изчаквания не са постоянни при рестартиране на сървъра, но можете да разрешите това, като стартирате метод за почистване при стартиране. Свързаните клиенти биха се върнали „онлайн“ доста бързо.
Този код се използва, когато изчакванията не трябва да са постоянни при рестартиране на сървъра
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'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)
});
});
В по-новите версии на node можете да използвате идентификатора на обекта Timeout
вместо самия обект, за да прекратите цикъла.
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 и да го съхраните, за да прекратите изпълнението на setInterval, вместо да се опитвате да съхраните целия обект.
Ето връзката към документите на възела: https://nodejs.org/api/timers.html#timers_timeout_symbol_toprimitive
setTimeout
има нещо общо с грешката в кръговата препратка. stackoverflow.com/questions/1493453 / - person Trevor   schedule 03.07.2012setTimeout
създава този обект:{ _idleTimeout: 1000000000, _idlePrev: { _idleNext: [Circular], _idlePrev: [Circular], ontimeout: [Function] }, _idleNext: { _idleNext: [Circular], _idlePrev: [Circular], ontimeout: [Function] }, _onTimeout: [Function], _idleStart: Mon, 02 Jul 2012 20:28:18 GMT }
- person user730569   schedule 03.07.2012_idleNext
и_idlePrev
изглеждат кръгови препратки... - person user730569   schedule 03.07.2012RedisStore
на socket.io -- вижте напр. този мой отговор. - person Linus Thiel   schedule 03.07.2012RedisStore
на Socket.io. Проблемът обаче се крие в съхраняването на върнатата стойност на методаsetTimeout
. - person user730569   schedule 03.07.2012disconnect
, но ако се върнат в рамките на X секунди, вземам таймера от хранилището и го изчиствам. В противен случай таймерът излъчва събитие за случайно прекъсване на връзката след тези X секунди. - person user730569   schedule 03.07.2012emit
към канала на това едно копие, което първоначално е обработило потребителя. Почти съм сигурен, че RedisStore ще се справи с разпространението към другите инстанции. - person Linus Thiel   schedule 03.07.2012publish/subscribe
нещата ли са? - person user730569   schedule 03.07.2012MemoryStore
по подразбиране, ако направите напр.io.sockets.of('foo').emit('foo', 'bar')
, който ще бъде изпратен до всички клиенти, свързани към това копие. ИзползвайкиRedisStore
, съобщението ще бъде изпратено до всички клиенти на всички инстанции, като се използватpublish/subscribe
нещата на redis. - person Linus Thiel   schedule 03.07.2012socket.join("channel name");
- person user730569   schedule 03.07.2012var socket = io.connect(); socket.join('foo');
ще се присъедини към стаята'foo'
, аvar socket = io.connect('/foo')
също ще се присъедини към стаята'foo'
. - person Linus Thiel   schedule 03.07.2012setTimeout
в redis, как ще мога да взема този обект и да изчистя времето за изчакване, когато клиентът се свърже отново? Има ли начин да се уверите, че клиентът се свързва отново със същия сървър на същия екземпляр? В противен случай новосвързаният сървър няма да може да намери обекта на таймера на предишния сървър, ако е в паметта с различен екземпляр и/или различен процес на възел, нали? - person user730569   schedule 28.11.2012