Da Fish in Sea

These are the voyages of Captain Observant

STOP Programming in Javascript

| Comments

You may be wondering about the title of this post… well, it isn’t the advice my grandfather gave me but I never heeded :) STOP is a reference to the concept of State and Transition Oriented Programming. I found out about this concept from Troy Gardner at the 360Flex conference a couple of years ago. The video of this presentation is well worth watching.

To summarize, in case you’re not done watching it yet.. States are a very important aspect of programming interactive systems, but they are often neglected and as a result can lead to nasty bugs. By dealing with states carefully and consciously it becomes a lot easier to develop more complex systems. Troy developed a framework in ActionScript to provide readymade support for state management and transitions.

It would be possible to port the framework to JavaScript, but the basic idea is so simple that it does not require a framework to implement and benefit from using it. This post is my attempt to describe how to do that.

Usually, from what I’ve seen, states are defined as constants, or enums or something like that, and assigned to some global variable…

1
2
3
4
if (isHoliday)
    STATE = "happy";
else
    STATE = "sad";

Then later on, there will be forks in the code to do different things depending on the state:

1
2
3
4
if (STATE === "happy")
    smile();
else if (STATE === "sad")
    frown();

While this works fine most of the time, you can quickly end up with a mess of duplciated conditional logic all over the place, as every time you need to do something, you have to check which State is active to decide if and how you’re going to do it.

It’s worth considering the difference between lowercase ‘state’ and uppercase State. The former is simply the values of variables that your application contains. This is vague and generalized and not what I’m referring to here. The uppercase State refers to a global condition which usually corresponds to a phase of activity of the application. For example a video player component might be ‘loading’ or ‘playing’ or ‘paused’. The State of the application fundamentally changes its behaviour, and how it will respond to events. Having said this, I’m not going to consistently capitalize ‘State’, but I’m always referring to the this more specific meaning of the word.

Events are things that happen in your application, whether it is user input or a result of processing or time passing. A collision between two objects is an example of a typical event in a game for example. Depending on which State the application is in, events will be handled differently. When events occur, we can send ‘messages’ or ‘signals’ to the currently active State, which can decide how to respond.

The idea that I’ve borrowed/stolen from Troy Gardner is to implement States as functions. Since they are essentially global constants, they are capitalized. This also indicates their special significance, and differentiates them from reqular functions. Here is how the two states of a light switch might be implemented (if we visualize the switch as having two buttons, ‘off’ and ‘on’):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var state = OFF;

offBtn.onClick = function () {
    state("turn_off");
}

onBtn.onClick = function () {
    state("turn_on");
}

function OFF(msg) {
    if(msg === "turn_on") {
        turnLightOn();
        state = ON;
    }
}

function ON(msg) {
    if(msg === "turn_off") {
        turnLightOff();
        state = OFF;
    }
}

Notice how the event handlers did not have to know about the different States of the application, but simply sent a message to the currently active State function, which is dynamically assigned when the state is set. Assigning the State function to the ‘state’ variable effectively sets the State, and alters the behaviour of the system… Note how the OFF State does not respond to the ‘turn_off’ message, and the ON State does not respond to the ‘turn_on’ message. There is no switching on the state value - rather the State function will handle the messages it is sent, ignoring any it is not interested in.

Transitions

It is often necessary to perform actions when changing from one State to another. But it can be easily done by using ‘enter’ and ‘exit’ messages which are passed to every State when they are made active or deactivated. The way to make this happen is to use a ‘changeState()’ function to change from one state to another:

1
2
3
4
5
function changeState(newState) {
    this.state('exit');
    this.state = newState;
    this.state('enter');
}

Then you can manage your transitions by listening for these messages in your State functions:

1
2
3
4
5
6
7
8
9
10
function DAY(msg) {
    if (msg === "enter") {
        wakeUp();
    } else if(msg = "exit") {
        turnLightsOn();
    }
}

changeState(DAY);
changeState(NIGHT);//lights get turned on

Hierarchical States

If you want to nest States, eg. LUNCH within DAY, this can be done by passing on any unknown messages to the super-state:

1
2
3
4
5
6
7
function LUNCH (msg) {
    if(msg === "eat") {
        eatLunch();
    } else {
        DAY(msg);//eg "check_email" msg would be handled by DAY
    }
}

If you have events which need to be handled at any time, they can be put in a BASE state, and other states can pass on messages to it. For example, if the user resizes the browser window, there could be a ‘resize’ message which would be handled by the BASE State:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function BASE (msg) {
    if (msg === "resize") {
        resizeGame();
    }
}

function PLAYING (msg) {

    if (msg === "scored") {
        increaseScore();
    }

    //always pass on messages to BASE
    BASE(msg);//resize will be handled

}

As you can see by comparing these two examples, you have the option of passing all messages to the super-state, or only passing on unknown messages.

This method of handling events by sending messages to State functions is very flexible and dynamic, and makes adding new States easy. It adds some abstraction between events and the response to them, which reduces coupling within your application. It also reduces duplication and conditional branching, and I think it also makes the code easier to read, as behaviours are grouped by State and message, which should be self-descriptive.

Finally, STOP programming is not opposed in any way to OOP, but is orthogonal to it. I have found it to be particularly useful in developing games, which tend to have many different states with different behaviours.

For a more complex example of this pattern in action, see the Asteroids game.