Finite State Machine in JavaScript
Tags: javascript
Published: 2020-03-29 16:50:27

A finite state machine is an abstract machine that can have only a fixed number of states. That means it has a fixed number of inputs and a set of outputs. A keyword finite represents that it will have limited options and memory to play with.
A state machine takes input and generates output - a new state:
```js
newState = input + currentSate
```
A simple example of a finite state machine could be a regular flashlight with one button. It has one input - a button. When you press it, the state changes from OFF to ON, and you see the light. When you press the same button again, it will change to OFF.
A single input can produce multiple states. Think of flashlight with several modes. On the first button press, you see the light; on the second press, the light starts to blink, and pressing the same button again, it'll switch off.
Such state transitions can be represented in the table:

|Input|Current state|Next state|
|---|---|---|
|PRESS|OFF|ON|
|PRESS|ON|BLINK|
|PRESS|BLINK|OFF|

## Structure

A state machine should be defined by the following properties:
- Initial state
- Actions for state transition
- Method to dispatch actions

The initial state is a default state when you initiate your machine. In the flashlight example, the initial state is OFF.
```js
const machine = {
    state: 'OFF',
    ...
};
```

 Actions define state transitions. Each action tells what should happen when it is invoked in a current state. For example, if the flashlight state was OFF, and we dispatch a PRESS action, then the machine will look at the current state, which is OFF, search for the defined actions, which is `press()` and invokes it. This will transition state from OFF to ON:
```js
const machine = {
    state: 'OFF',
    transitions: {
        OFF: {
            press() {
                this.state = 'ON'
            }
        },
        ON: {
            press() {
                this.state = 'BLINK';
            },
        },
        BLINK: {
            press() {
                this.state = 'OFF';
            },
        },
    },
    ...
};
```

To pass actions, we need a method. This method takes action name as an argument (additional arguments are optional if state logic is more complex ). When a dispatch method invoked, it looks in transitions, current state, searches for the dispatched action, and triggers it.
```js
const machine = {
    ...
    dispatch(actionName) {
        const action = this.transitions[this.state][actionName];
        
        if (action) {
            action.call(this);
        } else {
            console.log('Invalid action');
        }
    },
};
```

Putting it all together, we have a straightforward state machine that defines a sequence of state transitions depending on actions. 
```js
const machine = {
    state: 'OFF',
    transitions: {
        OFF: {
            press() {
                this.state = 'ON'
            }
        },
        ON: {
            press() {
                this.state = 'BLINK';
            },
        },
        BLINK: {
            press() {
                this.state = 'OFF';
            },
        },
    },
    dispatch(actionName) {
        const action = this.transitions[this.state][actionName];
        
        if (action) {
            action.call(this);
        } else {
            console.log('invalid action');
        }
    },
};

const flashlight = Object.create(machine);

console.log(flashlight.state); // OFF
flashlight.dispatch('press'); 
console.log(flashlight.state); // ON
flashlight.dispatch('press');
console.log(flashlight.state); // BLINK
```

We have created a new flashlight state object from the `machine` to inherit all the properties and methods. Then we dispatched action, named  'press', which triggered a response for a state change.
To invoke action function in `dispatch` we used `action.call(this)`. A method `Function.call()` provides a context (`this`) for an action function, that refers to a newly created `flashlight` object.

## Summing up

Finite state machines let you control the flow of your application state. It defines what should happen when specific actions are determined during the current state and make your application less error-prone.