Options
All
  • Public
  • Public/Protected
  • All
Menu

Feature Comparison

A quick look at what machines offer what functionality, across the 16 most popular FSMs on NPM at the time of writing. Updates and extensions are encouraged.

Definitions and a change link follow the tables.

Language features
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
States 16
Transitions 14
Actions 11
Data 6
TypeScript data 2
General hooks 7
Specific hooks 10
Post-hooks 4
Hook rejection 4
Transactions 1
Extending machines 3
Machine composition 3
Dynamic graphs 2
Properties 3
Methods 2
Weighted edges 1
Heirarchical states 3
State groups 2
Timeouts 4
Immediates 1
Error hooks 1
Input/output tape 1
Tape validator 1
Termination 6
Async transitions 4
Event emitter 3
Random walks 2
Serialization 2
Factories 4
Named instances 2
Automatic API 1
Count 19 13 10 11 10 2 8 6 7 5 4 5 4 12 4 6
Notations
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
String DSL 3
Wildcards 1
Stripes 0
Cycles 0
Kinds 0
State spread 1
Complex labels 0
Count 2 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0
API
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
In-place extrapolation 1
Graph reflection API 7
History 4
State histograms 1
Count 3 2 2 0 1 1 1 0 1 0 0 0 0 0 0 2
Docs, Support, and Community
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
Defined lifecycle 4
Detailed errors 2
Extend existing objects 2
Defined start states 6
Probabilistic starts 2
In-source debugger 1
Browser debugger 2
Compiler 0
Cross-compiler 0
Graph renderer 4
Visual styling 2
Manual 5
API samples 1
Demo videos 2
Tutorial videos 2
Chat community 3
Example library 2
Count 13 8 6 2 1 1 2 0 2 0 0 0 2 2 0 1
Testing
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
100% test coverage 1
Fuzz testing 1
Mutation testing 0
i18n testing 1
Count 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Tools
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
Live editor 2
CLI 1
VS Code Extension 1
Github Action 0
URL live-paste 1
Linter 0
Minifier 0
Count 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Totals
jssm XState javascript-state-machine machina finity fsm-iterator fsm-as-promised stately.js state-machine node-state fsm-event fsm stent robot3 mood grammar-graph
Language features 19 14 10 11 10 2 8 6 7 5 4 5 4 12 4 6
Notations 2 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0
API 3 2 2 0 1 1 1 0 1 0 0 0 0 0 0 2
Docs/support 13 8 6 2 1 1 2 0 2 0 0 0 2 2 0 1
Testing 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Tools 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Sum totals 43 26 18 13 12 4 11 6 10 6 6 5 6 14 4 9

 

 

Definitions

By section:

 

 

States
The bread and butter of a state machine - the states that the machine is permitted to occupy. It's hard to understand what a state machine that didn't support states would actually be. On a traffic light, we probably have red, yellow, green, and off.
Transitions
A machine that supports transitions allows you to specify which state to move to directly. Almost all machines support transitions. A handful of machines do not (usually by only supporting actions instead.) These are often called go, switch, change, state, set, or assign.
Actions
Actions are things that can be done, from a given state. These are distinct from transitions, which specify the end goal, by being a label specifying what's being done, instead; since these are also not part of the input or output alphabets, these are effectively a layer of indirection on behavior. What's useful here is the names can be repeated from different starting points. To progress in our traffic light without actions, we need to know what color we're on to ask for the successor by title; with actions, we can just teach each color the idea of next. Actions are sometimes called tasks, raise, signal, event, or do. In some machines these are mandatory; in FSL they are optional.
Data
Data is the difference between a Mealy and a Moore machine - data support means you can track more than just states. In a vending machine, having no data means you need states for every valid sum of coin values (one for five cents, one for ten cents, etc;) having data means you just track a number for what's already contained. Some machines call this context, or occasionally input.
TypeScript data
We say the machine supports TypeScript data if the machine's data object type is customizable, is exposed to TypeScript, and can be enforced by TypeScript. In this way, the machine's data is fully part of the TypeScript system.
General hooks
Hooks allow you to specify a function that gets called because of something that happened. Support for general hooks means that you can establish a hook on general or global events, such as "any transition" or "any event."
Specific hooks
Support for specific hooks means that you can establish a hook on particular states, transitions, or actions.
Post-hooks
Posthooks fire after a transition is complete, rather than before, and the data passed to the posthook reflects the later configuration. If you wanted to make an editor that visualized states' actions with buttons, you'd need to use posthooks, not hooks, so that the buttons were for what state they're now on, rather than the ones in the previous configuration.
Hook rejection
Support for hook rejection means that a given hook is allowed to deny a given behavior. An example is a state machine representing a user interface, which has a data member representing whether the user is logged in, and which disallows switching to the personal profile when not. Hooks that reject are sometimes called guards, and were called guards in earlier versions of this machine.
Transactions
In a transactional FSM, everything is transactional - if any hook in a process rejects, none of the other transformations that would have taken place do, and everything is rolled back to the end result of the last successful transition.
Extending machines
Support for extending machines means that an existing machine can be augmented in place, while keeping its state and any data intact. This is distinct from changing the source that made a machine and recompiling it; machine extension works on instances, not definitions.
Machine composition
Machine composition is either the combination of two machines, or the subordination of one machine to another using internal mechanisms. This is distinct from putting something together externally using hooks.
Dynamic graphs
In a machine which supports dynamic graphs, the structure of the machine can be changed while it is running, either in its states, its transitions, or its actions.
Properties
Support for properties means that states can and may be required to express named values. This can obviate repetitive switching to make decisions based on the state outside, and unify the behavior of things depending on machines under the machines' specification. A traffic light state machine's light color states might have properties regarding whether you may drive, or whether to go slowly.
Methods
Support for methods means that states may express named functions. Consider a state machine representing a network connection, which might be online or offline; it might express a lookup function which falls back to a local cache outside the presence of a network connection, but queries a backend when connected. This feature, when used fully, makes a state machine equivalent to Strategy Pattern.
Weighted edges
In a machine with weighted edges, transitions can be randomized, and some probabilities may be stronger than others. This allows machines to directly model simple probabilities, or probability meshes when used with random walks. Use of this feature makes a state machine equivalent to a First Order Markhov Chain.
Heirarchical states
Heirarchical states are a major approach to reducing the number of transitions in a machine, by allowing them to source from or target groups of states rather than individual states, frequently reducing a typical edge count from o(n^2) from state count down towards o(n) from group count. In an FSM representing a microwave, all states except idle will have an action for cancel, which could be reduced to the non-idle heirarchy. A limitation of heirarchies is that they generally cannot overlap, and groups frequently need to overlap.
State groups
Another method of reducing transition count is to allow the definition of arbitrary lists of states, and to treat them as heirarchical groups are treated, as valid source and endpoints. This is slightly more laborious, but also more flexible, and can be used to implement heirarchical FSMs directly.
Timeouts
A state with a timeout will, if unchanged and unacted, switch of its own volition to another state after a specified amount of time. Any transition or action automatically ends this timer. This is extremely helpful when implementing protocols, network behavior, enemy agent AI, or timed element demonstrations.
Immediates
In a machine with support for immediates, after a relevant transition, action, or hook to a target state, a new transition will automatically occur to a successor state with no delay. The most common uses for immediates are merging groups of paths and hooking the groups on the way through, inserting things into history, coursing during parsing and random construction, or construction of transfer states for things that wouldn't otherwise be allowed, such as multiple actions that (eventually) have the same source and destination states.
Error hooks
An error hook is a hook that's called when an error fires. Errors are distinct from refusals - asking to switch to a state that isn't allowed, or one that doesn't exist, are refusals, and should not fire this hook. Errors are for when you ask for things that don't make sense, such as a string with an opening quote but not a closing quote. Errors of that form are relatively rare in finite state machines, but can be important when dealing with data, dynamic graphs, or combined machines.
Input/output tape
This is the formal classical finite state machine (Σ,Γ,S,s0,𝛿,F) from the textbooks, which is defined as two alphabets, one set of transformations, an initial state, and two token streams. From this worldview on finite state machines, the input alphabet Σ is the things that are allowed to be on the input tape; the output alphabet is the your state list by default, but could be changed by your hooks; the set of states S is just the states you've defined; the set of transformations 𝛿 is your transitions, accepts an input symbol (from the tape) if Moore and also some data if Mealy; and the two streams are the input tape and the output tape. If you'd like to write a FSM as an acceptor or a validator, typically you would use these tape facilities. These tend to be found in parsing, iteration, and utility oriented machines. If you are only using the input tape and a halting state (by example, a machine that checks if the input is a number,) you create an acceptor; if you use the output tape to produce a transformed set of symbols (by example, an upper-casing machine,) you have instead made a transducer. Support for tape is quite rare, despite being high value.
Tape validator
A machine with tape validation has API to repeatedly use the same machine to validate a set of inputs through tape, without making the user implement the feed machinery repeatedly. These are found almost exclusively in parsing oriented machines.
Termination
Machine support for termination implies that a machine pays attention to when a state has no valid exits, frequently offering hooks or callbacks to let the machine user know that a machine has finalized. This is typically found in parsing and validation oriented machines.
Async transitions
Asynchronous transitions in machines typically mean that transitions may not be instantaneous, and that the result of a transition may be a callback, promise, or generator, instead of an immediately reflected change. This approach has tradeoffs. On the upside, the number of states being tracked is often significantly lower, and as such, the transition count quite a bit lower. On the other hand, this means that state machines may become locked and unavailable, introducing concurrency concerns, and requiring an api for mechanisms like is_changing. An alternative approach is to maintain the instantaneous API, and have states representing things underway, which is closer to the fundamental nature of an FSM, single threaded, and more precise, but also more verbose.
Event emitter
An event emitter emits Javascript events for transitions, actions, and so forth, as a convenient alternative way to notify the outside world besides hooks. As many Javascript tools consume events, this can remove a lot of dispatch boilerplate.
Random walks
Random walks allow you to wander over the possibilities in your state machine. Some state machines, like the canonical weather example, are well suited to using this directly; in others, this is a great way to validate that everything in your machine is reachable in a certain depth (particularly valuable for machines which represent user interfaces.) This is also frequently a constituent piece of generating state heatmaps.
Serialization
Serialization permits you to take the current state of a machine (with or without its definition, with or without history, always with data) into a string format which is safe for storage, and can be reliably unpacked again later. This is highly useful for save states, database storage, things moving through queues, and state exchange.
Factories
Factories allow you to create new instances of the same machine with other configurations quickly and easily, and make it straightforward to map a container as a set of configurations for new machines, or to treat a machine specification as a generator. Factories are useful when the same machine will be used in large numbers. An example would be the people in a game like Roller Coaster Tycoon - every time a new customer enters the park, the factory should spin off a new Person with a set of random preferences, clothes, money, and so forth.
Named instances
When re-using a machine frequently, such as with a factory method or a generator, it is often useful to name the instances so that you can tell them apart. By example, this can be useful when making network connections, parsing files in parallel, or when state machines represent assets in a system, such as the people and objects in a video game. As the number of machines you manage grows, so too grows the value of naming instances.
Automatic API
In a machine with an automatic API, transitions and/or actions are automatically added to the object's method namespace as functions, so that you don't need to call an indirection like .action('foo'), but instead just .foo(). This can be complex - one may need a slugging function, and collisions might become a problem. However, this can also yield more readable and usable machines, when done skillfully.

 

 

Updates

Mistake? Something out of date? New row or column needed?

Please let us know.

Generated using TypeDoc