6th February
2013
written by admin

Please note this is part of documentation I wrote for the opensource feather web framework currently available on GitHub. You can find the document in its original form here: https://github.com/maddogawl/feather/blob/master/featherdoc/docs/state%20machines.md

feather Finite State Machines

Finite State Machines (FSM) are well understood in computer science, and as such they won’t be covered much here. There are some really good articles online that you can check out to read more in depth analysis on FSM’s.

Links to some good articles

Wikipedia

State Machine Basics

In the simplest definition a FSM is a set of states and transitions. A FSM can only be in one state a time, thus allowing you to easily build state driven applications like Rich Internet Applications. In more complex systems that utilize state machines you will begin to see hierarchical state machines, and states that have their own state machines.

In general Finite State Machines are a tool that simplifies common problems including UI management and async flow.

Benefits of State Machines

An application could easily be created without utilizing FSM’s, but there are some clear advantages to using them.

Flexibility : Code can easily be tweaked and extended with proper use of state machines.

Debugging : Code that is isolated in small testable states makes it much easier to track down defects in the software.

Simplicity : Its human nature to think of things in terms of states. It is generally simple to break down parts of software in terms of states.

Example FSM

Let us start with a simpler example first to mainly demonstrate the syntax and structure for creating states and events. Also it is important to show how transitions are generated in feather state machines. The below example is just 3 states and two transitions, but it really touches on some of the basics that I believe are important.

javascript

var fsm= new feather.FiniteStateMachine({
    states: {
    initial: {
        stateStartup: function() {
            return this.states.enabled;
       }
    },
    enabled: {
       stateStartup: function( ) {
           //Enable here
       },
       clicked: {
          return this.states.disabled;
       }
    },
    disabled: {
       stateStartup: function( ) {
           //Disable Here
       }
    }
}});
									

Let’s walk through what is happening in this simple example. The FSM is created and by default enters the initial state. The event stateStartup is fired and a transition is generated by returning a state. Next the FSM enters the enabled event and waits for the clicked event before transitioning to the disabled state.

The FSM is first instantiated in the fsm member variable of me so communication with the fsm can occur which I will cover below. Then the states are declared, in this example we have a very simple FSM with initial, enabled, and disabled as the states. Each state has two very important optional events which are `stateStartup` and `leavingState`. In this simple example only `stateStartup` is utilized, but we could just as easily use `leavingState`.

FSM Communication

Sending events to a state machine requires an extra step that I need to cover. The above state machine will wait forever in the enabled sate unless the clicked event is sent to the state machine, so lets send it the clicked event when foo is clicked.

javascript

this.domEvents.bind(this.get("#foo"), "click", function( ) {
    fsm.fire("clicked");
});
									

foo specifies a HTML element by id, that is bound to the click event. The callback sends the “clicked” event to the state machine.

That is really it, just fire the event and the state machine will handle it if that event is implemented. This allows modelling of complex systems using a powerful design, while keeping the software more manageable and flexible.

A Real Example

The above example is an overly simplified use of a state machines, and in most cases just using a boolean would suffice for its functionality. So let’s demonstrate an example that utilizes state machines in a more meaningful way.

Here is an example that mimics a form submission, so let us go straight to the code, then it will be discussed below.

HTML

A textbox, checkbox, and button.

<input id="tutorialTextbox" type="text" />
<input id="tutorialCheckbox" type="checkbox" />I Agree
<button id="tutorialButton" disabled="disabled">Submit</button>
									

Client Side State Machine

javascript

var me = this;
//
//Form Submission Example
var button = me.get("#tutorialButton");
var checkbox = me.get("#tutorialCheckbox");
var textbox = me.get("#tutorialTextbox");
var fsm = new feather.FiniteStateMachine( {
states: {
    initial: {
        stateStartup: function( ) {
            return this.states.waiting;
        }
    },
    waiting: {
        stateStartup: function( ) {
            button.attr("disabled", true);
            checkbox.attr("disabled", false);
       },
       checked: function( ) {
           return this.states.enabled;
       }
    },
    enabled: {
        stateStartup: function( ) {
            button.attr("disabled", false);
            checkbox.attr("disabled", false);
        },
       unchecked: function( ) {
           return this.states.waiting;
       },
       click: function( ) {
           return this.states.validate;
       }
    },
    submit: {
        stateStartup: function( ) {
            var email = textbox.val();
           server_submitInfo([email], function (args) {
           fsm.fire("response", args);
       });
    },
    response: function(args) {
        return args.success ? this.states.success : this.states.error;
    }
},
    validate: {
        stateStartup: function( ) {

        //Disable all buttons while validation is happening
        button.attr("disabled", true);
        checkbox.attr("disabled", true);

        //Super complex validation that an email is believed to be valid could go here
        var txt = textbox.val();
        if(txt.length < 5) {
            return this.states.error;
        } else {
            return this.states.submit;
        }
    }
},
    error: {
        stateStartup: function( ) {
            //Show some sort of error indication here
            button.attr("disabled", false);
            checkbox.attr("disabled", false);

            //Place ERR in the textbox
            textbox.val("ERR");
           return this.states.enabled;
       }
    },
    success: {
        stateStartup: function( ) {
            me.get("#tutorialTextbox").val("Thank You");
       }
    }
}});
									

Client Side Binding

javascript

//Bind Events
    me.domEvents.bind(me.get("#tutorialButton"), "click", function(args) {
        fsm.fire("click");
    });

    me.domEvents.bind(me.get("#tutorialCheckbox"), "click", function(args){
        this.checked ? fsm.fire("checked") : fsm.fire("unchecked");
    });
									

First off no real validation is occurring in my example, the client checks to make sure there is a string input with 5 or more characters into the text field and then sends it to the server. The server implementation can be anything you want it to be.

Begin by defining the states.

initial : Required as the starting state.

waiting : This state enables the checkbox control, disables the button, and waits until the checkbox is checked.

enabled : This state waits until the button is clicked, and transitions to the validate state.

validate : Client side validation, this can be as complex as you would want it to be. Transitions to the submit or error state.

submit : Sends the data to the server and waits for a response. Based on the response the state machine will either transition to error state or success state.

error : Generic error state that any error causes a transition to.

success : The final state once the form is validated and finished.

The next step before implementation was to define the transitions. In more complex systems its best to layout a decision tree so its easy to see how the transitions occur, but in this case its simple enough to just write it down. Remember though that state machines can quickly grow so keeping it documented can make it much easier when looking for faulty code or adding new states.

This example state machine is easy enough to understand but it needs to have a few points explained. Even though this is being used primarily for UI management in this example its also very easy to see how it can be used for async flow just by seeing how the call to the server is handled in the submit state. An event is fired from the callback to the state machine, and everything is handled from there. You could easily build a state machine to better represent the async calls which keeps your code cleaner and easier to maintain. Another place that that state machines really help is the centralizing of error handling.

In this example its easy to see that each piece of functionality is separated, so extending the functionality or tracking down a defect is much easier to accomplish.

Advanced Use

Useful Functions

FSM’s in feather implement the following functions

gotoPreviousState : Switches the state machine back to the previous state.

gotoInitialState : Transitions to the Initial state in the state machine.

getCurrentStateName : Returns the name of the current state which is mainly useful for test cases and debugging.

onceState : Allows additional events to be fired when a state machine enters a state, the event triggers once and then is discarded.

onState : Allows additiona events to be fired when entering states, with options for immediate and one time execution.

getStateName : Returns the state name based on the state reference passed in.

Inside Feather

The feather framework utilizes FSM’s throughout, each Widget is derived from FiniteStateMachine. FiniteStateMachine is derived from EventPublisher which provides the fire method.

Comments are closed.

RECENT POSTS

31st December 2014
03rd August 2014
25th June 2014