Skip to content

Instantly share code, notes, and snippets.

@ismasan
Created February 8, 2011 01:39
Show Gist options
  • Save ismasan/815680 to your computer and use it in GitHub Desktop.
Save ismasan/815680 to your computer and use it in GitHub Desktop.
Basic State Machine for Javascript objects
//++++++++++++++++++++++++++++++++++++++++++++++++
// Basic FSM implementation
//++++++++++++++++++++++++++++++++++++++++++++++++
var StateMachine = function(initial_state) {
var transitions = {},
state_callbacks = {enter:{}, exit:{}},
addTransition = function (from, to, guard) {
transitions[from] = transitions[from] || {};
transitions[from][to] = guard || function () {return true};
},
addCallback = function (clbk, state, handler) {
state_callbacks[clbk][state] = state_callbacks[clbk][state] || [];
state_callbacks[clbk][state].push(handler)
},
runCallbacks = function (obj, clbk, state, other_state) {
var b = state_callbacks[clbk][state];
if(!b) return true;
for(var i=0;i<b.length;i++){
b[i].apply(obj, [other_state]);
}
}
return {
state: initial_state,
onStateChange: function (state) {
return true;
},
transition: function (from, to, guard) {
addTransition(from, to, guard);
return this;
},
enter: function (state, handler) {
addCallback('enter', state, handler);
return this;
},
exit: function (state, handler) {
addCallback('exit', state, handler);
return this;
},
goTo: function (new_state) {
var from = transitions[this.state];
var to = from ? from[new_state] : false;
if(!to){
throw 'No transition defined from "'+this.state+'" to "'+new_state+'"';
}
if(to()) { // run guard function
runCallbacks(this, 'exit', this.state, new_state);
runCallbacks(this, 'enter', new_state, this.state);
this.state = new_state;
this.onStateChange(new_state);
return true;
} else {
return false;
}
}
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++
// USAGE
//++++++++++++++++++++++++++++++++++++++++++++++++
function exampleGuard() {
console.log('RUNNING GUARD');
return true; // false to deny transition
}
var Connection = function(){}
Connection.prototype = StateMachine('closed');
var conn = new Connection();
conn.onStateChange = function(state){console.log('SUCCESSFUL STATE CHANGE TO ', state)}
conn
//+++++++++++++++++++++++++++++++++++++++++++++
// TRANSITIONS
//+++++++++++++++++++++++++++++++++++++++++++++
.transition('closed', 'failed' )
.transition('closed', 'connecting', exampleGuard)
.transition('connecting', 'unavailable' )
.transition('connecting', 'connected' )
.transition('unavailable','connected' )
.transition('connected', 'unavailable' )
//+++++++++++++++++++++++++++++++++++++++++++++
// CALLBACKS
//+++++++++++++++++++++++++++++++++++++++++++++
.exit('closed', function (to) {
console.log('EXITING closed TO ', to);
})
.enter('connecting', function (from) {
console.log('ENTERING connecting FROM ', from);
})
.exit('connecting', function (to) {
console.log('EXITING connecting TO ', to);
})
try{
conn.goTo('connected');
} catch(e) {console.log(e, 'CURRENT STATE IS', conn.state)}
conn.goTo('connecting');
console.log(conn.state);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment