Javascript State Machines — A Tutorial

Venkat Peri
7 min readOct 7, 2018

--

State machines are ideal for programming tasks that involve a sequence of operations and logic that dictates which operation will be executed after the current one based, for example, on data you’re managing, inputs from the user, responses from pending tasks, timeouts and other events.

Classes of Automata

Automata theory categorizes state machines into four major types (see figure), of which, Finite State Machine (FSM) form the bulk of software implementations.

However, FSMs only report their current states, which means data and and actions that form the total state of app are scattered.

In this tutorial, we’re going to create a single page web app that simulates the the control panel of a hotel safe whose logic is implemented in a Turing machine which gives us the full capability of a FSM as well as co-located data and actions . We’ll use:

  • Vue framework for the single page app and the safe’s user interface,
  • gen-statem framework for the Turing machine/logic of the safe.

Click here for a hosted version of the result.

Ye Olde Hotel Safe

If you’ve stayed in a hotel, chances are you’ve come across a small safe in your room where you can keep your small valuables safe when you go out and about.

Get the Code

Get the code for this tutorial from Github and install the dependencies (you’ll need an existing installation of node.js):

git clone https://github.com/venkatperi/hotelsafe
cd hotelsafe
npm install

Launch webpack’s dev server:

npm run dev

To create a webpack optimized build (available in dist/):

npm run build

Deploying in Production

The hosted version of this tutorial is deployed via netlify. When the github repo for the tutorial is updated, netlify builds the production version with npm run build and deploys it from the dist/ directory.

Hotel Safe — the State Machine

gen-statem is a framework for building Turing machines in Javascript. In this example, we’ll subclass the StateMachine class to provide an interface to the safe. The safe’s initial state is open.

This section is in Typescript.

The data type for our state machine is:

  • codeSize: The code length (e.g. 4 digits).
  • code: Stores the unlock key when locked.
  • input: Stores the user’s input during unlocking.
  • timeout: Timeout for events and message display (5s).
  • message: The message to be shown on the display.

The state machine data, along with the current state of the machine, forms the total state of our app. gen-statem provides a data proxy hook to easily synchroinze the total state with frameworks such as React.

Event Handlers

We’ll program the state machine by giving it an ordered list of event handlers. When gen-statem receives an event, it matches the event (and the state machine’s current state) with the handler’s keys and invokes the first matching handler.

Our safe’s event handlers, in order, are:

  • The safe is open. Clear all data when we enter the open state and tell the state machine to stay in the current state (keepState).
  • In open state, if the user presses RESET its time to get the new code. Update the displayed message and go to open/locking state.
  • In open/locking state, if the user presses a digit, we get a cast#button/<digit>... event. Record the four more recent digits. This is our unlock code. Update the display to show the code. Finally, tell the state machine to repeat the current state, which keeps the state the same, but the state machine will fire an enter event (for the same state). Down the line, we have a enter handler which starts an eventTimer to help us time out on inactivity.
  • In open/locking state, the user presses LOCK: ignore it if the code is too short. Otherwise, flash the code and lock the safe by going to closed/success. Further down, we have an enter handler for closed/success which will set a genericTimer to make the state machine eventually transition to closed.
  • The safe is closed. Clear any old unlock code any time we enter closed.
  • In closed, if the user presses a button, postpone that event and go to closed/unlocking. The reason is that we’ll start an eventTimer any time we enter closed/unlocking which will help us time out on lack of activity. By postponing the current event button, we’ll capture it in the next state and not lose it.
  • We’re listening for a unlock code. If the code is too short, do nothing. Otherwise, compare against the stored code and unlock if the code matches. Otherwise show an error and stay locked.
  • If we enter a */locking or */unlocking state, start the eventTimeout timer so we can track lack of activity.
  • If we enter a state that showing a message, start a genericTimeout so that we can remove the message when it fires.
  • We’re using complex states, so we get an eventTimeout or genericTimeout in a sub-state, just go back to the root state.

State Diagram

Here’s the state diagram for the state machine:

Hotel Safe: State Diagram

Testing the State Machine

Its easy to test the state machine by simulating inputs, for example with mocha, independent of the UI:

npm test
Mocha BDD test for timeout on inactivity

The Single Page App

Our single page app uses the Vuejs framework and we need to tell Vue how to render it:

main.js

We’re going to take a detailed look at the safe’s UI (Safe.vue) and how it interfaces with the safe’s state machine.

Safe.vue

The Safe Vue component declares the following data items:

  • safe: the safe’s state machine.
  • message: the text displayed on the safe.
  • state: the current state of the state machine (for debugging).

When the safe’s state machine is created, we attach a state listener to pull the current message and state and store it in the component:

We’ll add a few interface methods that will send events to the state machine:

Finally, we can hook up the UI with the state machine:

What’s Next?

In this tutorial, we looked the hood of a single page app of a safe’s control panel rendered by Vuejs and controlled by a gen-statem state machine. Click here for a hosted version of the app.

Widgets like safes, locks and turnstiles are excellent candidates for talking about state machines since its easy to relate to their states, inputs and transitions and its highly likely that their embedded control systems are based on state machines.

State machines aren’t limited to hardware or merely the stuff of real world things. Its quite likely your next app is a candidate for a state machine if it involves a set of operations, and decisions about what to do next based, as we discussed in the introduction. What’s needed though, is a bit of planning and sketching of a state diagram to extract the control flow; not a huge hurdle, considering programmers love to design before jumping into coding, no?

--

--

Venkat Peri

Seasoned engineering leader with 29 years of experience. Expert in team building, large-scale SaaS product development, data-driven business solutions.