FSL's biggest benefit is ease of use, from short machines. However, it's not much value to just say that; instead, we should see what the actual difference is, by comparisons.
When possible, all of these comparisons are taken from the comparison product's
documentation, and are generally unchanged; when not, by following something
that was; and sometimes to add include
or require
to make runnable code.
Sometimes details like labels or constancy will be altered to match for
comparison; if so, this will be pointed out.
The JSSM examples are not golfed. For example, on the states of matter machine, one could hook all actions, and print from an object whose property names were the state names, to get that down to two lines; this is the expected "natural" way to write it, instead.
All code samples are formatted with prettier
for fairness.
Numbers in bold represent official code; numbers not in bold are examples I
wrote, and despite good faith, may not represent ideal notation. If the text
is
Libraries are sorted shortest-average first, with failing libraries sorted to the end.
Library | Tog | Traf | Matt | Avg |
---|---|---|---|---|
jssm | 1 | 2 | 5 | 2.66 |
state-machine | 5 | 8 | 14 | 9 |
faste | 4 | 14 | 24 | 10.66 |
javascript-state-machine | 7 | 13 | 23 | 14.33 |
finity | 7 | 10 | 28 | 15 |
stately | 8 | 18 | 24 | 16.66 |
robot | 17 | 24 | 31 | 24 |
xstate | 16 | 36 | 33 | 28.33 |
8 | 12 | |||
20 | 26 |
In essence, a simple light switch. Just shows the basics of making states, and linking them with actions.
lib | length |
---|---|
jssm | 1 |
faste | 4 |
state-machine | 5 |
finity | 7 |
javascript-state-machine | 7 |
stately | 8 |
8 | |
xstate | 16 |
robot | 17 |
20 |
jssm
toggle machine, 1 lineexport const toggleMachine = sm`active 'TOGGLE' <=> 'TOGGLE' inactive;`;
xstate
toggle machine, 16 linesFrom their documentation
export const toggleMachine = createMachine({
id: "toggle",
initial: "inactive",
states: {
inactive: {
on: {
TOGGLE: "active",
},
},
active: {
on: {
TOGGLE: "inactive",
},
},
},
});
javascript-state-machine
toggle machine, 7 linesExported and consted.
export const toggleMachine = new StateMachine({
init: "inactive",
transitions: [
{ name: "toggle", from: "inactive", to: "active" },
{ name: "toggle", from: "active", to: "inactive" },
]
});
finity
toggle machine, 7 linesFinity did not have a light switch example. I made this, following this unrelated machine as a style guide.
I don't format finity
with prettier
because prettier
does an unreasonably
bad job with the oddly nested callback structure. This isn't finity's fault.
export const toggleMachine = Finity
.configure()
.initialState('inactive')
.on('toggle').transitionTo('active')
.state('active')
.on('toggle').transitionTo('inactive')
.start();
stately
toggle machine, 8 linesStately did not have a light switch example. I made this, following this unrelated machine as a style guide.
export const toggleMachine = Stately.machine({
inactive: {
toggle: "active",
},
active: {
toggle: "inactive",
},
});
nanostate
toggle machine, 8 linesRobot did not have a toggle example. I made this, following this unrelated machine as a style guide.
export const toggleMachine = nanostate("inactive", {
inactive: {
toggle: "active",
},
active: {
toggle: "inactive",
},
});
robot
toggle machine, 17 linesRobot did not have a toggle example. I made this, following this unrelated machine as a style guide.
const toggleMachine = createMachine(
{
inactive: state(
transition(
"toggle",
"active"
)
),
active: state(
transition(
"toggle",
"inactive"
)
),
},
() => true
);
faste
toggle machine, 4 linesTaken from the readme. Renamed, bound, and exported the machine result; changed the labels.
onClick = () => this.setState( state => ({ enabled: !state.enabled}));
export const toggleMachine = faste()
.on('toggle', 'inactive', ({transitTo}) => transitTo('enabled'))
.on('toggle', 'active', ({transitTo}) => transitTo('disabled'))
state-machine
toggle machine, 5 linesNo toggle machine was available; wrote from scratch and used the docs for usage guidelines.
var toggleMachine = new StateMachine({
transitions: [
'toggle : inactive > active > inactive'
]
});
machina
toggle machine, 20 linesNo toggle machine example was available; wrote from scratch and used the
pedestrianSignal
example in their landing page
for usage guidelines.
export const matter = new machina.Fsm({
initialState: "inactive",
states: {
uninitialized: {
"*": function () {
this.deferUntilTransition();
this.transition("inactive");
},
},
inactive: {
_toggle: "active",
},
active: {
_toggle: "inactive",
},
},
toggle: function () {
this.handle("_toggle");
},
});
Three state, no off
, no flashing red
. Emit a console log of 'Red light!'
whenever the red state is entered.
Shows the basics, as well as putting a hook on a state (or a node in some systems' lingo.)
lib | length |
---|---|
jssm | 2 |
state-machine | 8 |
finity | 10 |
javascript-state-machine | 13 |
faste | 14 |
stately | 18 |
robot | 24 |
xstate | 36 |
12 | |
26 |
jssm
traffic light, 2 linesexport const trafficLight = sm`red 'next' => green 'next' => yellow 'next' => red;`;
trafficLight.hook_global_action("next", () => console.log("Red light!"));
xstate
traffic light, 36 linesThere's most of a traffic light between their documentation here and also here, and it seems to piece together into this:
export const trafficLight = createMachine(
{
initial: "green",
states: {
green: {
on: {
next: {
target: "yellow",
},
},
},
yellow: {
on: {
next: {
target: "red",
},
},
},
red: {
entry: "alertRed",
on: {
next: {
target: "green",
},
},
},
},
},
{
actions: {
alertGreen: (context, event) => {
alert("Green!");
},
},
}
);
finity
traffic light, 10 linesfinity
did not have a traffic light example. I made this, following this
unrelated machine
as a style guide.
Finity does not appear to support hooking specific transitions, but instead offers a single global transition hook.
I didn't format this with prettier
, because prettier
does a really bad job
with the chain .state().on().transitionTo()
; the length doubles and this
becomes unreadable, and that isn't finity
's fault.
const matter = Finity
.configure()
.initialState('red')
.onEnter(() => console.log('Red light!'))
.on('next').transitionTo('green')
.state('green')
.on('next').transitionTo('yellow')
.state('yellow')
.on('next').transitionTo('red')
.start();
stately
traffic light, 18 linesstately
did not have a traffic light example. I made this, following this
unrelated machine as a style
guide.
export const matter = Stately.machine({
red: {
onEnter: () => console.log("Red light!"),
next: () => {
return this.green;
},
},
green: {
next: () => {
return this.yellow;
},
},
gas: {
next: () => {
return this.red;
},
},
});
javascript-state-machine
traffic light, 13 linesjavascript-state-machine
did not have a traffic light example. I made this,
from scratch.
export const matter = new StateMachine({
init: "red",
transitions: [
{ name: "next", from: "red", to: "green" },
{ name: "next", from: "green", to: "yellow" },
{ name: "next", from: "yellow", to: "red" },
],
methods: {
onRed: function () {
console.log("Red light!");
},
},
});
nanostate
traffic light, 12 linesTaken from the readme:
Changed the name of the event from timer
to next
; exported and consted.
Reordered to start in red, instead of to start in green.
Added a red light hook with .on
.
export const trafficLight = nanostate("red", {
red: {
next: "green",
},
green: {
next: "yellow",
},
yellow: {
next: "red",
},
});
trafficLight.on('red', () => console.log('Red light!'));
robot
traffic light, 24 linesRobot did not have a traffic light example. I made this, following this unrelated machine as a style guide.
Robot does not appear to support hooks on nodes, so we've faked it with hooks on transitions.
export const trafficLight = createMachine(
{
red: state(
transition(
"next",
"green"
)
),
green: state(
transition(
"next",
"yellow"
)
),
yellow: state(
transition(
"next",
"red",
action(() => console.log("Red light!"))
)
),
},
() => true
);
faste
traffic light, 14 linesTaken from the readme. Only change was to rename and export the variable.
export const trafficLight = faste()
.withPhases(["red", "yellow", "green"])
.withTransitions({
green: ["yellow"],
yellow: ["red"],
red: ["green"],
})
.withMessages(["switch"])
.on("switch", ["red"], ({ transitTo }) => transitTo("green"))
.on("switch", ["green"], ({ transitTo }) => transitTo("yellow"))
.on("switch", ["yellow"], ({ transitTo }) => {
console.log("Red light!");
transitTo("red");
});
state-machine
traffic light, 8 linesNo traffic light was available; wrote from scratch and used the docs for usage guidelines.
export const trafficLight = new StateMachine({
transitions: [
'next : red > green > yellow > red'
],
handlers: {
'red' : () => console.log('Red light!')
}
});
machina
traffic light, 26 linesAdapted from the pedestrianSignal
example in their landing page.
export const trafficLight = new machina.Fsm({
initialState: "red",
states: {
uninitialized: {
"*": function () {
this.deferUntilTransition();
this.transition("red");
},
},
green: {
_next: "yellow",
},
yellow: {
_next: "red",
},
red: {
_next: "green",
_onEnter: function () {
console.log("Red light!");
},
},
},
next: function () {
this.handle("_next");
},
});
Three basic states of matter. Hook each of the four transitions with chatter on follow.
In addition to the basics, shows how to put a hook on a transition (or an action or an edge, in other machines' terminology.)
lib | length |
---|---|
jssm | 5 |
state-machine | 14 |
javascript-state-machine | 23 |
stately | 24 |
faste | 24 |
finity | 28 |
robot | 31 |
xstate | 33 |
jssm
states of matter, 5 linesexport const matter = sm`solid 'melt' <=> 'freeze' liquid 'vaporize' <=> 'condense' gas`;
trafficLight.hook_global_action('melt', () => console.log('I melted'));
trafficLight.hook_global_action('freeze', () => console.log('I froze'));
trafficLight.hook_global_action('vaporize', () => console.log('I vaporized'));
trafficLight.hook_global_action('condense', () => console.log('I condensed'));
xstate
traffic light, 33 linesxstate did not have a states of matter example. I made this, following this unrelated machine and this one, and also this one as style guides.
export const matter = createMachine({
initial: "solid",
states: {
solid: {
on: {
melt: {
target: "liquid",
actions: () => console.log("I melted"),
},
},
},
liquid: {
on: {
freeze: {
target: "solid",
actions: () => console.log("I froze"),
},
vaporize: {
target: "gas",
actions: () => console.log("I vaporized"),
},
},
},
gas: {
on: {
condense: {
target: "liquid",
actions: () => console.log("I condensed"),
},
},
},
},
});
javascript-state-machine
states of matter, 23 linesUsed the example found here.
Changed the variable name, exported, and consted.
export const matter = new StateMachine({
init: "solid",
transitions: [
{ name: "melt", from: "solid", to: "liquid" },
{ name: "freeze", from: "liquid", to: "solid" },
{ name: "vaporize", from: "liquid", to: "gas" },
{ name: "condense", from: "gas", to: "liquid" },
],
methods: {
onMelt: function () {
console.log("I melted");
},
onFreeze: function () {
console.log("I froze");
},
onVaporize: function () {
console.log("I vaporized");
},
onCondense: function () {
console.log("I condensed");
},
},
});
finity
states of matter, 28 linesfinity
did not have a states of matter example. I made this, following this
unrelated machine
as a style guide.
Finity does not appear to support hooking specific transitions, but instead offers a single global transition hook.
I didn't format this with prettier
, because prettier
does a really bad job
with the chain .state().on().transitionTo()
; the length doubles and this
becomes unreadable, and that isn't finity
's fault.
const matter = Finity
.configure()
.initialState('solid')
.on('melt').transitionTo('liquid')
.state('liquid')
.on('vaporize').transitionTo('gas')
.on('freeze').transitionTo('solid')
.state('gas')
.on('condense').transitionTo('liquid')
.global()
.onTransition( (fromState, toState) => {
switch (fromState) {
case 'solid':
console.log('I melted');
break;
case 'liquid':
if (toState === solid) {
console.log('I froze');
} else if (toState === gas) {
console.log('I vaporized');
}
break;
case 'gas':
console.log('I condensed');
break;
}
})
.start();
stately
states of matter, 24 linesstately
did not have a states of matter example. I made this, following this
unrelated machine as a style
guide.
export const matter = Stately.machine({
solid: {
melt: () => {
console.log("I melted");
return this.liquid;
},
},
liquid: {
freeze: () => {
console.log("I froze");
return this.solid;
},
vaporize: () => {
console.log("I vaporized");
return this.gas;
},
},
gas: {
condense: () => {
console.log("I condensed");
return this.liquid;
},
},
});
nanostate
states of matter, 15 lines, ❌ cannot implementnanostate
did not have a states of matter example. I made this, following
this unrelated machine
as a style guide.
nanostate
does not appear to support on-action hooks, and does not appear to
pass the previous state when calling its global enter hook. Therefore there is
no way to correctly implement the hooks leading to liquid - condense and melt -
because you can't tell whether they're coming from solid or gas. On these
grounds, nanostate
cannot implement this machine correctly.
export const trafficLight = nanostate("solid", {
solid: {
melt: "liquid",
},
liquid: {
freeze: "solid",
vaporize: "gas",
},
gas: {
condense: "liquid",
},
});
trafficLight.on("solid", () => console.log("I froze"));
trafficLight.on("gas", () => console.log("I vaporized"));
trafficLight.on("liquid", () =>
console.log("❌ FAIL: cannot tell if melt or condense")
);
robot
states of matter, 31 linesrobot
did not have a states of matter example. I made this, following this
unrelated machine as a style
guide.
const matter = createMachine(
{
solid: state(
transition(
"melt",
"liquid",
action(() => console.log("I melted"))
)
),
liquid: state(
transition(
"freeze",
"solid",
action(() => console.log("I froze"))
),
transition(
"vaporize",
"gas",
action(() => console.log("I vaporized"))
)
),
gas: state(
transition(
"condense",
"liquid",
action(() => console.log("I condensed"))
)
),
},
() => true
);
faste
states of matter, 24 linesfaste
did not have a states of matter example. I made this, following this
unrelated machine as a style
guide.
export const matter = faste()
.withPhases(["solid", "liquid", "gas"])
.withTransitions({
solid: ["liquid"],
liquid: ["solid", "gas"],
gas: ["liquid"],
})
.withMessages(["melt", "freeze", "vaporize", "condense"])
.on("melt", ["solid"], ({ transitTo }) => {
console.log("I melted");
transitTo("liquid");
})
.on("freeze", ["liquid"], ({ transitTo }) => {
console.log("I froze");
transitTo("solid");
})
.on("vaporize", ["liquid"], ({ transitTo }) => {
console.log("I vaporized");
transitTo("gas");
})
.on("condense", ["gas"], ({ transitTo }) => {
console.log("I condensed");
transitTo("liquid");
});
state-machine
states of matter, 14 linesNo states of matter example was available; wrote from scratch and used the docs for usage guidelines.
export const matter = new StateMachine({
transitions: [
"melt : solid > liquid",
"freeze : solid < liquid",
"vaporize : liquid > gas",
"condense : liquid < gas",
],
handlers: {
"@melt": () => console.log("I melted"),
"@freeze": () => console.log("I froze"),
"@vaporize": () => console.log("I vaporized"),
"@condense": () => console.log("I condensed"),
},
});
machina
states of matter, 36 lines, ❌ cannot implementNo states of matter example was available; wrote from scratch and used the
pedestrianSignal
example in their landing page
for usage guidelines.
machina
does not appear to support on-action hooks, and does not appear to
pass the previous state when calling its global enter hook. Therefore there is
no way to correctly implement the hooks leading to liquid - condense and melt -
because you can't tell whether they're coming from solid or gas. On these
grounds, machina
cannot implement this machine correctly.
export const matter = new machina.Fsm({
initialState: "solid",
states: {
uninitialized: {
"*": function () {
this.deferUntilTransition();
this.transition("solid");
},
},
solid: {
_melt: "liquid",
},
liquid: {
_freeze: "solid",
_vaporize: "gas",
},
gas: {
_condense: "liquid",
_onEnter: function () {
console.log("Red light!");
},
},
},
melt: function () {
this.handle("_melt");
},
freeze: function () {
this.handle("_freeze");
},
vaporize: function () {
this.handle("_vaporize");
},
condense: function () {
this.handle("_condense");
},
});
Generated using TypeDoc