You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
Stately.js is a JavaScript based finite-state machine (FSM) engine for Node.jsand the browser.
Installation
In Node.js you can install Stately.js withnpm:
$ npm install stately.js
and include it to your project by:
varStately=require('stately.js');
Alternately, you can install Stately.js withbower:
$ bower install --save Stately.js
In browsers you can include it directly by adding it to the document head section:
<scripttype="text/javascript"src="https://raw.github.com/fschaefer/Stately.js/master/Stately.js"></script><scripttype="text/javascript">// use Stately</script>
or with Asynchronous Module Definition by e.g.:
<scripttype="text/javascript"src="https://raw.github.com/jrburke/requirejs/master/require.js"></script><scripttype="text/javascript">require(['https://raw.github.com/fschaefer/Stately.js/master/Stately.js'],function(Stately){// use Stately});</script>
Usage
Creating a new machine
A new state machine can be created with either the new operator:
Both will return a newstateMachine object, with all events from all statesattached to it. The machine will transition into the initial stateinitialStateNameor the first attachedstateObject ifinitialStateName is omitted. In additionto the events thestateMachine object has agetMachineState() method, returningthe current name of the machines state,getMachineEvents(), returning possibleevents in the current state.
ThestatesObject is an object withstateObject objects attached asproperties.The property names of thestatesObject are thestates of the machine.The attachedstateObject objects model the machines states with the propertynames asevents and the connected functions asactions:
If different states use the same event identifier, theevents are chained upand the machine handles calling the correctaction for the current state (iftheevent is handled in the current state). If the event is not handled inthe current state, it is ignored.
If no immediateaction needs to be declared, the desired transitionstatecan be attached to theevent as string directly:
There are several ways anaction can transition the machine into anotherstate. The simplest form is returning the desired next state from an action.Therefore,this refers to the (internal)stateStore inside anaction toaccess the other states of the machine:
...'STATE1':{ doSomething:function(){ ...//transition from STATE1 to STATE2returnthis.STATE2;// as an alternative just return the new state as string// return 'STATE2';}}...
If an action should not transition the machine into another state, just omitthe return value (or return the current state).
Sometimes it is desired to return a value from an action. In this case thereturn value must be an array with two elements. The first element is the nextstate the machine should transition into, and the second element the returnvalue:
...'STATE1':{ doSomething:function(){ ...//transition from STATE1 to STATE2 and return a stringreturn[this.STATE2,'this is a return value'];}}...
For asynchronous actions there aregetMachineState() andsetMachineState(nextState) accessible through thethis reference of anaction:
Once in a while, it is useful to get anotification when the machinetransitions into another state. Therefore there are some special event namesreserved for event functions, namelyonEnter,onLeave (triggeredwhen entering / leaving a state),onBefore<eventname> andonAfter<eventname>(triggered before or after calling an event).
The event function has the following signature:
function(event,oldState,newState){ ...}
event - The event that triggered the transition.oldState - The old state the machine is transitioned from.newState - The new state the machine is transitioned into.
Inside these functions,this refers to the internalstateStore.
Examples
Door
vardoor=Stately.machine({'OPEN':{'close':/* => */'CLOSED'},'CLOSED':{'open':/* => */'OPEN','lock':/* => */'LOCKED'},'LOCKED':{'unlock':/* => */'CLOSED','break':/* => */'BROKEN'},'BROKEN':{'fix':function(){this.fixed=(this.fixed===undefined ?1 :++this.fixed);returnthis.fixed<3 ?this.OPEN :this.BROKEN;}}});//the initial state of the door is open (it's the first state object)console.log(door.getMachineState()==='OPEN');// true;//close and lock the doordoor.close().lock();console.log(door.getMachineState()==='LOCKED');// true;//try to open itdoor.open();console.log(door.getMachineState()==='OPEN');// false;//unlock, open, lock (is ignored because it fails), close, and lockdoor.unlock().open().lock().close().lock();console.log(door.getMachineState()==='LOCKED');// true;//the door is still locked, break itdoor.break();console.log(door.getMachineState()==='BROKEN');// true;//fix opens the door, close it, lock it, break it againdoor.fix().close().lock().break();console.log(door.getMachineState()==='BROKEN');// true;//and again fix opens the door, close it, lock it, break itdoor.fix().close().lock().break();console.log(door.getMachineState()==='BROKEN');// true;//fixing is limited, the door stays brokendoor.fix();console.log(door.getMachineState()==='OPEN');// false;console.log(door.getMachineState()==='BROKEN');// true;