Demons, oh no!
Don't you worry, we're talking about Daemons here, not Demons. This blog post will be about an object oriented method of creating a timer, communicating with that timer and maintaining its state. I love timers, having code execute at an interval or at least at a controlled pace feels great if it adds a cool feel to an interactive website or design. Timers are powerful!
Okay, so... A Daemon you say?
Indeed, a Daemon. Not really a Daemon as some might know from Unix-based systems, but simply a background process within your application. Other parts of the application should be able to probe it for information and manipulate its state.
As usual, I'll cite Wikipedia on traditional Daemons:
In multitasking computer operating systems, a daemon (/ˈdiːmən/ or /ˈdeɪmən/)[1] is a computer program that runs as a background process, rather than being under the direct control of an interactive user. Traditionally daemon names end with the letter d: for example, syslogd is the daemon that implements the system logging facility and sshd is a daemon that services incoming SSH connections.
Another interesting note on the terminology:
The term was coined by the programmers of MIT's Project MAC. They took the name from Maxwell's demon, an imaginary being from a famous thought experiment that constantly works in the background, sorting molecules.
What will we make?
Simply put: A class that allows for timers to be created without initially starting them. Each timer can have a userdata value (e.g. an object which can be accessed from the timer function). Each timer has a message queue, which holds a list of queued data objects (any type). These messages will only be removed from that queue when they are read.
passive or active timing
The Daemon class should have a way to specify whether the timing should happen immediately each time the timed function is executed, or after it has executed. This means that when passive timing is enabled, the next call will only happen x-milliseconds after the function executed. Active timing means that the next call will happen x-milliseconds, regardless of whether the function has executed yet.
Exceptions
When using object oriented patterns, one should always consider using exceptions. Exceptions make checking for errors easier, even though it might slow some code down. Don't throw plenty exceptions when you have some method with a heavy algorithm, however do it in places where logic needs to be enforced.
For our code, we'll be adding an exception named DaemonException. The code:
/**
* DaemonException will be thrown from constructor
* @param {String} message the error description
*/
var DaemonException = function DaemonException(message) {
this.name = "DaemonException";
this.message = message;
};
We can now throw a custom exception when something goes wrong in our books. Let's build a constructor that handles all initial information for us. It will take a function, a timeout in milliseconds, any user data and an argument to enable passive timing.
/**
* Create a new daemon object
* @param {Function} f The daemon function
* @param {Number} timeout the interval in milliseconds
* @param {Object} [data=null] userdata to send to the function you provide
* @param {Boolean} [passive=true] enable or disable passive timing mode
* @returns {Object} When called without `new` - a Daemon object is returned
*/
var Daemon = function Daemon(f, timeout, data, passive) {
data = data || null;
passive = passive || true;
// create an instance if this function was called regularly
if(!(this instanceof Daemon)) {
return new Daemon(f, timeout, data);
}
// make sure a function is provided
if(typeof(f) !== "function" || !(f instanceof Function)) {
throw new DaemonException("daemon function not an instance Function");
}
// we need a number for the timeout
if(typeof(timeout) !== "number") {
throw new DaemonException("timeout not a number (needs to explicitly match, ===)");
}
// configure the object
this.function = f; // the function to execute
this.timeout = timeout; // the timeout for our interval
this.data = data; // may be null or undefined!
this.state = false; // current state (running or paused)
this.messages = []; // the message queue
this.passive = passive; // passive timing mode, which means start timing after function call.
};
Adding methods
We now have our class constructor and a nice exception we can throw when something goes wrong. We now need to extend the prototype of Daemon in order to add some useful methods. We're going to add multiple methods, so it might be useful to temporarily copy a reference of the prototype in a variable that's easier to type:
var p = Daemon.prototype;
It's easier to type p.methodName =
instead of Daemon.prototype.methodName =
.
The start() method
Firstly we'll create a method which allows the Daemon to be started. When constructed, the Daemon does not immediately start the internal timer - you need to start it by calling the start method. This method should check the state property and return immediately when it is already running.
It should then define a timer function which will be called every ~x-milliseconds, depending on the timing mode. If the passive mode is enabled, it should call setTimeout after the function has finished executing. If the passive mode is disabled, it should call setTimeout immediately each time the timer function is called.
The timer function should then be called to cause the timer to start. The Daemon is then running.
The code for start():
/**
* start executing the daemon until it is stopped, at the specified interval.
* the interval starts counting right after the execution of the daemon code
* has finished. This means 300ms doesn't really mean each 300ms, but it means
* 300ms plus the time it takes for `f` to execute
*/
p.start = function() {
// don't start again when the Daemon is already running.
if(this.state) {
return;
}
var that = this;
that.state = true;
// anonmyous self-invoking functions are not allowed in strict mode, so we name it
var timer = function DaemonTimer() {
if(!that.passive && that.state) {
// active timing mode, immediately time the next call
that.timer = setTimeout(timer, that.timeout);
}
/**
* if the state is still true the function should be called with `this` set to the
* Daemon instance. If the function returns false explicitly, the Daemon should pause
* execution.
*/
if(that.state === false || that.function.call(that) === false) {
that.state = false;
return;
}
if(that.passive) {
// re-call the timer function (which is this function) after `timeout` has passed
that.timer = setTimeout(timer, that.timeout);
}
}; timer(); // invoke
};
The stop() method
This one is quite simple as it only has to stop the timer. It needs to attempt to clear out the timeout which was set earlier and set the state to false, only if the state was true. Here's the code:
/**
* stop the daemon
*/
p.stop = function() {
if(!this.state) return; // already stopped.
clearTimeout(this.timer);
this.state = false;
};
The callNow() method
This method simply invokes the timed function immediately and will stop the Daemon when the timed function returns false (just like how it will behave automatically). The code:
/**
* call the daemon code immediately, might interrupt timer.
*/
p.callNow = function() {
if(this.function.call(this) === false) {
this.state = false; // still stop it when explicitly called - gracious stop
}
};
The isRunning() method
This one is really really simple, it is simply a getter for the state property. It returns true when the Daemon is running and false when it is not. See the full code for its code.
Message queue methods
The message queue management is based on a few Array functions that are quite easy to use.
- sendMessage pushes a value onto the messages array.
- countMessages returns the length of the messages array.
- readMessage shifts a message off of the messages array (stack) and returns it. This method returns false when no message is available.
- clearMessages removes all messages in the queue.
Examples
Here are some pens that show basic usage of the Daemon class, when it might be useful and when managing such timers might be desireable.
Example #1 - A Very simple backgroundColor Daemon
Check out this pen.
Example #2 - A very simple message queue example
Check out this pen.
Example #3 - Time and background color
I must add that this one was inspired by this awesome pen, and the idea was slightly altered. The resulting hex color is lightened a bit.
Check out this pen.
I might add more examples soon, which keep specific datasets up to date
Full code
All code has been discussed, let's wrap it up in a module wrapper to keep things local! Download: Daemon.js
That's it, thanks for reading! This is just me writing code to get these awesome things called timers into these awesome things called objects, in a simple way. Cheers!