Created
August 23, 2012 15:27
-
-
Save egonelbre/3437746 to your computer and use it in GitHub Desktop.
State Machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Machine(first, $){ | |
var cur = {}, next = $[first]; | |
var self = { | |
go : function(to){ next = next ? next : ($[to] || $.undefined); }, | |
trigger : function(event){ var t = cur.tx && cur.tx[event]; t && self.go(t); } | |
}; | |
return function(){ | |
if(next){ | |
cur.exit && cur.exit.call(self); | |
cur = next; next = undefined; | |
self.__proto__ = cur.data; | |
cur.enter && cur.enter.call(self); | |
} | |
cur.fn && cur.fn.call(self); | |
}; | |
}; | |
var m = Machine("tick", { | |
undefined : { | |
fn : function(){ | |
console.log("ERROR: NO STATE!"); | |
this.trigger("recover"); | |
}, | |
tx : { | |
recover : "tick" | |
} | |
}, | |
tick : { | |
enter : function(){ | |
console.log("entered tick"); | |
}, | |
fn : function(){ | |
console.log("tick"); | |
this.go("tock"); | |
} | |
}, | |
tock : { | |
data : { | |
counter : 0 | |
}, | |
exit : function(){ | |
this.counter += 1; | |
}, | |
fn : function(){ | |
this.counter += 1; | |
console.log("tock : " + this.counter); | |
if(this.counter > 5) | |
this.go("invalid"); | |
if(this.counter > 3) | |
this.trigger("forward"); | |
}, | |
tx : { | |
forward : "tick" | |
} | |
} | |
}); | |
console.log("========================="); | |
for(var i = 0; i < 15; i += 1) | |
m(); |
Thanks, bro.
I tried to write a typesafe version which is written in Typescript.
This is the transpiled result:
var tsCustomError = require('ts-custom-error');
class StateMachine {
_currentState;
_states;
_logic;
constructor(states, initialState, logic) {
this._states = states;
this._currentState = initialState;
this._logic = logic;
}
getAllStates() {
return [...this._states];
}
getCurrentState() {
return this._currentState;
}
_verifyState(value) {
if (!this._states.includes(value)) {
throw new tsCustomError.CustomError(`[StateMachine][_verifyState]: value "${value}" is not defined in initial states!`);
}
}
switchBy(...factors) {
const newState = this._logic(this._currentState, ...factors);
this._verifyState(newState);
this._currentState = newState;
return this._currentState;
}
}
exports.StateMachine = StateMachine;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wouldn't recommend writing this convoluted code, but the short explanation is:
$
inMachine
keeps all the different states.3.1 check whether we need to go to a different state, if yes:
3.1.1 then call the exit method on the state
3.1.2 then change the state
3.1.3 change the
self
data to point to the current state3.1.4. call the enter on the state
3.2. call the state function
self
acts the object for holding the state and different methods. The methods arego
, which changes the state directly to another. The other one istrigger
which looks up the event name fromtx
and then advances to that state.Changing the
data
thatthis
has is done via changing out the._proto_
inheritance.Here's a slightly simpler version of it: