Set and Clear Timeout on Node Server from Client

I'm attempting to allow a user to set an alarm from the client and pass it to the server. The server then has a setTimeout that counts down and when time runs out, executes the function.

This first part is working fine, however, I need the the ability to clear that same timeout, should the client decide to cancel that particular alarm.

Note: I've been storing various data using Redis, so that is available.

var client = redis.createClient();
io.set("store", new sio.RedisStore);

io.sockets.on('connection', function (socket) {

socket.on('alarm:config', function(list, date, time, bool) {

  if (bool) {

    var now = new Date().getTime();

    var year = date[0],
        month = date[1] - 1,
        day = date[2];

    var hour = time[0],
        minutes = time[1];

    var alarm = new Date(year, month, day, hour, minutes);

    var countdown = alarm - now;

    var myAlarm = setTimeout(function() {

      // do stuff...

    }, ( countdown ) );

  } else {

    clearTimeout(myAlarm);

  }

});
});

The approach I have in mind is that I would use the boolean value to determine if the user is setting or canceling that particular alarm. I realize that setting a local variable "myAlarm" will not work, I just put it there to convey the idea.

I am trying to figure out a way to store a reference to that exact timeout so that the next time the "alarm:config" socket event is triggered with a false boolean value, it can cancel the timeout that was set earlier.

It might be another question all together, but how does an application like Google Calendar store a date and time and then know exactly when to trigger it as well as offer the ability to cancel it? This would essentially be the same idea.

UPDATE: I have it working using the following solution. I am open to a more elegant solution.

socket.on('alarm:config', function(list, date, time, bool) {

  var alarmName = "timer:" + list;

  if (bool) {

    client.hset(alarmName, "status", true);

    var now = new Date().getTime();

    var year = date[0],
        month = date[1] - 1,
        day = date[2];

    var hour = time[0],
        minutes = time[1];

    var alarm = new Date(year, month, day, hour, minutes);

    var countdown = alarm - now;

    setTimeout(function() {

      client.hget(alarmName, "status", function(err, bool) {

        if(bool == 'true') {

          // do stuff...

        } else {

          console.log("This alarm has been canceled.");

        }

      });

    }, ( countdown ) );

  } else {

    console.log('canceling alarm');
    client.hset(alarmName, "status", false);

  }
});

Depending on how large of an application you're building, there are a couple of options.

Processing Queue

You could restructure your application to use a job queue instead of simply setting timers. This has an advantage that you can split it in the future into multiple processes, but it does complicate the handling a bit.

A library like Kue uses just Redis and allows you to do a delayed put to set events in the future.

Going from the Kue readme:

var kue = require('kue')
  , jobs = kue.createQueue();

// Create delayed job (need to store email.id in redis as well)
var email = jobs.create('email', {
    title: 'Account renewal required',
    to: 'tj@learnboost.com',
    template: 'renewal-email'
}).delay(minute).save();

// Process job
jobs.process('email', function(job, done){
  email(job.data.to, done);
});

// Cancel job
email.remove(function(err){
  if (err) throw err;
  console.log('removed completed job #%d', job.id);
});

Storing Reference to Timeout

This is much less robust, but could allow you to do it very simply. It leaves global variables floating around, and has no recovery if the process crashes.

Basically, you store the timer in a global variable and cancel it based on the job id.

var myAlarms = {}

socket.on('alarm:config', function(list, date, time, bool) {
  var alarmName = "timer:" + list;

  if (bool) {

    myAlarms[alarmName] = setTimeout(function() {
      // do stuff...
    }, countdown);

  } else {
    clearTimeout(myAlarms[alarmName]);
  }
}

Note: None of this code has been tested and it likely contains errors.