JavaScript and finite-state machine

Finite-state machine is a very useful model that can simulate most of the things in the world.

Simply put, it has three characteristics:

  • The number of the state is limited.
  • At any moment, a state machine can only in one state.
  • Under certain conditions, a state machine can transit from one state to another.

Its meaning for JavaScript is that many objects can be written as finite state machines.

For example, the page has a menu element. When the mouse hover, the menu is displayed; when the mouse is removed, the menu is hidden. If you use a finite state machine description, this menu is only two states (show and hide), the mouse will lead to state changes.

The code can be written as follows:

  const menu = {
    currentState: 'hide',

    initialize: function() {
      var self = this;
      self.on("hover", self.transition);
    },

    transition: function(event){
      switch(this.currentState) {
        case "hide":
          this.currentState = 'show';
          doSomething();
          break;
        case "show":
          this.currentState = 'hide';
          doSomething();
          break;
        default:
          console.log('Invalid State!');
          break;
      }
    }
  }

You can see that the simple implementation of a finite-state machine is logically clear and very expressive, which is kind of suitable for encapsulating events. The more states and events an object has, the more suitable for using finite-state machine.

In addition, JavaScript is an language with a lot of asynchronous operations, a common usage is to use callback functions, but this will cause confusing code structure, known as “callback hell” – which is difficult to test and debug. The finite state machine provides a better way to hook the asynchronous operation to the state change of the object, and when the asynchronous operation is finished, a corresponding state change occurs, which in turn triggers other operations. This is more logically clear and easier to reduce the complexity of the code than using callbacks, event monitoring, publish / subscribe, and others.

The following describes a finite state machine function library Javascript Finite State Machine . This is a powerful, easy to understand finite-state machine library in javascript.

The library provides a global object StateMachine, using the object’s create method, you can generate a finite state machine instance.

  const fsm = StateMachine.create();

When generating, you need to provide a parameter object that describes the nature of the instance. For example, traffic lights can be described as follows:

  const fsm = StateMachine.create({

    initial: 'green',

    events: [
      { name: 'warn',  from: 'green',  to: 'yellow' },
      { name: 'stop', from: 'yellow', to: 'red' },
      { name: 'ready',  from: 'red',    to: 'yellow' },
      { name: 'go', from: 'yellow', to: 'green' }
    ]
  });

The initial status of the traffic signal is green, the events attribute is the collection of events that trigger a state change, such as the warn event causes the green state to be yellow, and the stop event causes the yellow state to become red and so on.

After generating the instance, you can query the current status at any time.

  • fsm.current: returns the current state.
  • fsm.is (s): Returns a boolean indicating whether state s is the current state.
  • fsm.can (e): Returns a boolean indicating whether event e can be triggered in the current state.
  • fsm.cannot (e): Returns a boolean indicating whether event e can not be triggered in the current state.

Javascript Finite State Machine allows two callback functions to be specified for each event, with the warn event as an example:

  • onbeforewarn : Triggered before the warn event occurs.
  • onafterwarn (can be abbreviated as onwarn): Triggered after the warn event occurs.

At the same time, it also allows two callback functions to be specified for each state, taking the green state as an example:

  • onleavegreen : Triggered when leaving the green state.
  • onentergreen (can be abbreviated as ongreen): Triggered when entering the greeen state.

Assuming that the warn event causes the state to change from green to yellow, the above four types of callback functions occur in the following order: onbeforewarn → onleavegreen → onenteryellow → onafterwarn.

In addition to assigning a callback function individually for each event and state, you can also specify a generic callback function for all events and states.

  • onbeforeevent: Triggered before any event occurs
  • onleavestate: Triggered when leaving any state.
  • onenterstate: Triggered when entering any state.
  • onafterevent: Triggered after any event occurs.

If the event callback has asynchronous operations (such as Ajax), then we may wish to wait until the asynchronous operation is finished, and then change the state. Here we need to use the transition method

  fsm.onleavegreen = function(){
    light.fadeOut('slow', function() {
      fsm.transition();
    });
    return StateMachine.ASYNC;
  };

In the callback shown above, there is an asynchronous operation (light.fadeOut). If you do not want the state to change immediately, let the callback return StateMachine.ASYNC, which means that state temporarily does not change. After the asynchronous operation is completed, then call the transition method to make the state change.

Javascript Finite State Machine also allows you to specify an error handler that automatically triggers when an event that should not occur in the current state occurs.

  const fsm = StateMachine.create({
    // ...
    error: function(eventName, from, to, args, errorCode, errorMessage) {
      return 'event ' + eventName + ': ' + errorMessage;
    },
    // ...
  });

For example, the current state is green, theoretically only warn events can occur at this time. If a stop event occurs in that case, it will trigger the above error handling function.

Javascript Finite State Machine basic usage is the above, a more detailed description can refer to its home page.

Be the first to comment

Leave a Reply

Your email address will not be published.


*