Try to define the JavaScript component by using the finite state machine approach

Source: Internet
Author: User
Tags fsm

This article is a learning article, learning to use the idea of finite state machine to define the method of JavaScript components, welcome to read, follow-up plan will write a few specifically to use the finite state machine to help themselves to write the components of the blog, to prove that the idea of the value of programming implementation is currently being actively conceived. This article code download

1. Finite state machine Overview

Simply put, finite state machine is a model, models are used to simulate things, can be finite state machine model simulation of things, generally have the following characteristics:

1) can use state to describe things, and at any one time, things are always in a state;

2) The total number of things the state has is limited;

3) by triggering certain behaviors of things, you can cause things to transition from one state to another;

4) changes in the state of things are regular, a state can be transformed to B,b can be transformed to c,a but not necessarily can change to C;

5) The same behavior can change things from a variety of states to the same state, but not from the same state into multiple states.

For example, a switch component that simulates a check button can be described with a state machine:

varSwitch =function($elem) {varLog =function(FSM, previousstate) {Console.log (' CurrentState is: ' + Fsm.currentstate + ((previousstate | | ") && (', and previous state is: ' +( previousstate)));    }; return{currentstate:' Off ', states: {' On ': {to:' Off ', Action:' Turnoff '            },            ' Off ': {to:' On ', Action:' TurnOn '}}, init:function () {            varSelf = This; $elem. On (' Click ', (function () {                varargs =arguments; return function() {self.transition (args);            }            })()); Log ( This); }, Transition:function(e) {varOld = This. CurrentState;  This. CurrentState = This. states[old].to; varAction = This. states[old].action; (Actioninch  This) && This[Action] (old); }, TurnOn:function(fromstate) {$elem. addclass (' On '); Log ( This, fromstate); }, Turnoff:function(fromstate) {$elem. Removeclass (' On '); Log ( This, fromstate); }    }};

In this simple example, the switch component has 2 states, on and off, either on or off, with an initial state of off, with 2 behaviors: Turnoff and Turnon, which can change the component from the on State to the off state, The latter can make the component from the off state to the on State, its behavior is bound to the Click event of a DOM element, the following is I use this JS (switch.js) to run with jquery, click the button three times after the results (corresponding to the source of switch.html):

You can see that the initial state of this component is printed after the call to S.init (), and when clicked once, the component transitions from the off state to the on State, and after the second click the switch from the on state to the off state, and then the third time back to the on state. This example is an extremely simple state machine implementation, but it can be more appropriate to explain the state machine's thinking and its advantages (logical thinking clearly, strong expression ability). In the actual work, we can use Javascript-state-machine to implement the component based on the state machine, it is a JS implementation library of the finite state machine model, it can quickly define a state machine object, compared with the implementation of the example I wrote earlier, Although the source of this library is only more than 200 lines, but the function is very complete, the API is easy to use, it is worth learning and practice.

2. Using the Javascript-state-machine Library to implement the state machine

As long as the introduction of the library JS can be provided through the library of a global object StateMachine, and using the object's Create method, to generate a finite state machine instance (from the library official document traffic lights example):

Example 1 (corresponds to demo1.html):

In this example: The initial option is used to represent the initial state of an FSM object, and the events option is used to describe the rules of change for all states of an FSM object, each of which corresponds to a behavior (although it may be possible for multiple rules to correspond to the same behavior, as you will see in the following example). The Create method adds a method to each behavior of the instance, and invoking this method is equivalent to triggering an object's behavior, and the state of the object can change when the object's behavior occurs. The example created above will have the following behavior method:

Fsm.warn ()-Call this method, the instance state will change from ' green ' to 'Yellow '-Call the method, the instance state will change from ' Yellow 'to ' red '-call this method, the instance state will change from ' Red ' to ' Yellow '-Call this method, the instance state will change from ' yellow ' to ' green '

These methods are created automatically according to the events rules configured at create time, and the method name corresponds to the name attribute in the events rule, and there are several statemachine names in the events rule, and several behavior methods are added. It also adds the following members to determine and control the state and behavior of the instance for ease of use:

Fsm.current-----Returns the list of behaviors that can be triggered in the current state of an instance as an array

When you print this object on the console, you can see all the members of this object:

Remember the things that are listed above that can be used with finite state machine models, and then the use case to illustrate how the Javascript-state-machine created objects meet the requirements of the state machine model:

1) can use state to describe things, and at any one time, things are always in a state

The traffic light instance created in this example is either in the yellow State or in the red state, or in green, so it satisfies the 1th.

2) The total number of things the state has is limited

This instance has a maximum of three states.

3) by triggering certain behaviors of things, you can cause things to transition from one state to another.

Fsm.warn,fsm.panic,fsm.cal,fsm.clear These behavioral methods can change the state of an instance.

4) changes in the state of things are regular, a state can be transformed to B,b can be transformed to c,a but not necessarily can change to C

The initial state of this instance is green, and according to the state change rules of the events configuration, green can be transformed to yellow, yellow can be transformed to red, but after the instance is initialized, You cannot call Fsm.panic This behavior method, because this method can only be called when the instance state is yellow, and the state of the instance is green at the time of initialization, so only the Warn method can be called at first:

The panic method can be called when the Warn method is called, causing the object's state to change from green to yellow.

5) The same behavior can change things from a variety of states to the same state, but not from the same state into multiple states

This example does not explain this very well, because its state change rules do not have the same behavior, from a variety of States to a certain state of the rules, but this example is sure to meet this requirement, because its change in the configuration of the rule, there are 4 kinds of behavior defined, Each behavior can only be changed from one state to another, and there are no situations in which there are many states before and after the transformation. In addition, from the theory is also very good understanding this point, why not from the same state into a variety of states, because the 1th said that things can only be in a state at any moment, if a certain behavior makes the state of things become a variety, the state mechanism of things have problems.

Here is another official example to illustrate the same behavior, which can be transformed from a variety of states to a state of the scene:

Example 2 (corresponds to demo2.html):

This example feels simulated by a person whose meaning is expressed very clearly: it simulates this person with four states hungry, satisfied, full, sick, respectively, representing hunger, joy, fullness, sickness, the initial state of hungry, this man has 2 behaviors eat and rest, respectively stands for eating and resting, as long as this person begins to eat, its state from the hungry to become happy (people hungry when there is something to eat can not be happy), then eat, the state from happy to full, if eat more, this person will be ill, whether the person is hungry or full, is happy or sick, as long as it is lying in that rest, will eventually be hungry. In contrast to Example 1, this example:

1) Although it is configured with multiple change rules, it has only 2 behaviors (the number of distinct name (values) in the events configuration indicates how many behaviors the state machine has);

2) Its eat behavior after the state is related to the current state, the current state is different, the behavior occurs after the state is also different, so the eat behavior corresponds to a number of configuration rules;

3) The state of its rest behavior has nothing to do with the current state, as long as the current state is within the state condition of the rest behavior, the result of the behavior is the same, so the rest behavior uses a from array to configure the condition range of the current state in which the behavior occurs. The entire behavior defines only one configuration rule.

In the actual use of the state machine instance, we invoke the behavior of the instance to trigger a change in the state of the instance, such as Example 1: Fsm.warn (), so that the state of the FSM will be changed from green to yellow, like this simple state machine instance, this degree of use may be sufficient, But for real projects, the components we define tend to use the instances they generate to do a lot of complex logic, and if you define components with state machines, where are these logic codes written? Because Javascript-state-machine creates a state machine instance, its behavior is automatically added, you cannot rewrite these behavior methods, otherwise you lose the meaning of the state machine (the logic of state change and the business logic are split). The answer is a callback. Javascript-state-machine defines the relevant callbacks before and after the transformation of each state of each instance and before and after the transformation of each behavior, and your logic can be written in these callbacks, thus achieving the purpose of state logic and business logic splitting. Let's take a look at the use of these callbacks, and then I'll use javascript-state-machine to rewrite the example of the switch component of the previous analog check box.

Javascript-state-machine Depending on the configuration of events, there are 4 types of callbacks that can be defined for an instance:

Onbeforeevent_name----triggers after the corresponding behavior ofevent_name occurs

Of these, Event_Name is specified by the name contained in the events configuration rule Name,from to include, each callback can receive three parameters:

Event--the state after the Acthas occurred

The two callbacks of Onbeforeevent_name and Onafterevent_name must be triggered after each behavior of the state machine is triggered. The onenterstate callback that corresponds to the state of the onleavestate and the state after the occurrence of the behavior must also be triggered (as long as these callbacks are defined), and the callback order is consistent with the order listed earlier.

In Example 2 we can define these four types of callbacks in the following way:

Open the page in the browser, call the fsm.eat in the console, you can see the following print results:

You can also see the order of callbacks according to the order in which they are printed:

Onbeforeeatonleavehungryonentersatisfiedonaftereat

The previous four callbacks correspond to four types, the state is different, or the event is not the same, the callbacks that need to be defined differ, preceded by callbacks for the hungry,satisfied and eat behavior definitions, and can define callbacks for Full,sick and rest behavior. You can also define callbacks for Onenterhungry and Onleavesatisfied, and the actual application to define which callbacks to write logic code, depending on the requirements, Javascript-state-machine triggers these four types of callbacks based on the events rule when the corresponding behavior occurs.

In addition, Javascript-state-machine defines four general callbacks, four callbacks that have no relation to event,state, and when any behavior triggers, any state changes, the associated callbacks are triggered, and the four callbacks are:

onbeforeevent----triggers after any actionhas occurred

The four callback names are fixed and have no relation to the triggered behavior and the state to be changed, which is equivalent to a global callback. That is, if a state change rule related to four types of callbacks are defined and the four global callbacks are also defined, and the four global callbacks are also defined, then triggering the corresponding behavior of the rule will trigger a total of 8 callbacks, the order of 8 callbacks is (in case 2 of this rule to explain {name : ' Eat ', from: ' Hungry ', to: ' Satisfied '}):

Onbeforeeatonbeforeeventonleavehungryonleavestateonentersatisfiedonenterstateonaftereatonafterevent

These callbacks can be initialized with the callbacks option passed to create during initialization, or can be added or modified (corresponding to demo3.html) by modifying the properties of the instance directly:

Nullfunction(event, from, to) {    Console.log (' status changed! , before changing: ' + from + ', after change: ' + to ' ;}

Operation Result:

You can see the members of this instance in the console:

Phase ratio 1 The member of the traffic light instance that is printed, the member of Example 2 instance, in addition to the behavior method and Example 1, there are also many of these callback members starting with on, Example 1 is not, that is because example 1 is not configured with callbacks.

With that in front of you, you can use Javascript-state-machine to rewrite the previous switch components (corresponding to switch2.html):

varSwitch =function($elem) {varLog =function(from, to) {Console.log (' CurrentState is: ' + to + (from | | ") && (', and previous state is: ' +from )); }, FSM=Statemachine.create ({initial:' Off ', events: [{name:' TurnOn ', from: ' Off ', to: ' On '}, {name:' Turnoff ', from: ' In ', to: ' Off '}], callbacks: {onafterturnon:function(event, from, to) {$elem. addclass (' On ');                    Log (from, to); }, Onafterturnoff:function(event, from, to) {$elem. Removeclass (' On ');                    Log (from, to);    }                }            }        );    ; $elem. On (' Click ',function() {fsm[fsm.transitions () [0]]();    });    Log (undefined, fsm.current); returnFSM;};

How to use:

<script src= "Js/jquery.js" ></script><script src= "lib/javascript-state-machine-master/ State-machine.js "></script><script src=" js/switch2.js "></script><script>    var  New Switch ($ (' #btn-switch ')); </script>

The effect is the same as before:

In actual work, it is certain that during the behavior trigger, Javascript-state-machine provides 3 ways to cancel the behavior, because some conditions do not allow the need to cancel the behavior, so that the object state is changed incorrectly:

return False in Onbeforeevent_name callback to cancel the currently triggered behavior return false in the Onleavestate callback can also cancel the currently triggered behavior in the onleavestate callback Statemachine.async to perform asynchronous behavior

The first two methods, return false in the specified callback can cancel the behavior, the third method returns only one asynchronous identity, whether the cancellation behavior needs to be further specified in the callback of the asynchronous task. This method works well for those with asynchronous tasks, meaning that when this behavior triggers, it does not trigger the change in the state of the object at the same time, but waits until the asynchronous task executes and then changes the state, citing the official example to illustrate the scenario of this asynchronous task:

varFSM =Statemachine.create ({initial:' Menu ', events: [{name:' Play ', From: ' Menu ', to: ' Game '}, {name:' Quit ', from: ' Game ', To: ' Menu '}], callbacks: {onentermenu:function() {$ (' #menu ')). Show (); }, Onentergame:function() {$ (' #game ')). Show (); }, Onleavemenu:function() {      $(' #menu '). FadeOut (' Fast ',function() {fsm.transition ();      }); returnStatemachine.async;//Tell StateMachine to defer next state until we call transition (in FadeOut callback above)}, Onleavegame:function() {      $(' #game '). Slidedown (' Slow ',function() {fsm.transition ();      }; returnStatemachine.async;//Tell StateMachine to defer next state until we call transition (in Slidedown callback above)    }  }});

This example creates an instance that contains play and quit two behaviors that, after triggering, do not immediately change the state of an object, but instead open an asynchronous animation task and then, after the animation ends, by invoking the transition method of the instance: Fsm.transition () To notify the instance to change its state. To tell the FSM that the current execution is an asynchronous behavior, it needs to be handled in the onleavestate callback, such as Onleavemenu, Onleavegame, by return Statemachine.async. In addition, in the callback of the end of the asynchronous task, if you want the FSM to change state, notify it via Fsm.transition (), but if after the end of the asynchronous task, because some conditions are not allowed, or if you want to cancel the behavior, you can call Fsm.cancel () to notify it So that the FSM cancels the current asynchronous behavior and the object state does not change.

This asynchronous programming approach is similar to jquery's deferred objects:

function foo (URL) {    var defer = $. Deferred ();    $.ajax ({        url:url    }). Done (function(res) {        = = 200? ' Resolve ': ' Reject '] ();    }). Fail (function() {        defer.reject ();    });     return $.when (defer)}

Finally about Javascript-state-machine can also be described in this article is the error option, in the Create instance, you can use this option to specify a callback, so that when the current state should not trigger the behavior, the FSM does not throw an error, Instead, this error is given to the callback specified by error to handle, otherwise it will directly throw the error to the browser, which will certainly lead to the function of the component can not be used, so if you want to use Javascript-state-machine, this callback must be added, Even simply print some information (corresponding to demo4.html):

Add Error callback:

Operation Result:

Do not add error callbacks:

Operation Result:

The introduction of Javascript-state-machine to this end, in the official document also has 2 sections also has the use of the scene, the library is interested in the recommendation to learn the official documents ~

3. Summary

1) The finite state machine is a useful mode for defining components, which makes the code of the component look clearer and easier to understand;

2) Javascript-state-machine is also an excellent implementation of the library, the source is concise, provide a simple API usage, but also highlight the characteristics of the state machine, it is worth in defining the components of the time to try;

3) Finite state machine This model is suitable for components with obvious state characteristics;

4) When using Javascript-state-machine, you can either define the component directly on the basis of the FSM, or you can retain an FSM (internal state machine) within the component through a private member;

5) The examples given in this article are not close enough to the actual project, and will look at the projects that have been done in the near future to use the state machine model to rewrite the module, then write a blog to share with you.

Thank you for reading:)

This article code download

Try to define the JavaScript component by using the finite state machine approach

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.