好好学习,认真工作

javascript实现有限状态机

1、状态机描述

简单说,有限状态机是一种模型,模型都用来模拟事物,能够被有限状态机这种模型模拟的事物,一般都有以下特点:

1)可以用状态来描述事物,并且任一时刻,事物总是处于一种状态;

2)事物拥有的状态总数是有限的;

3)通过触发事物的某些行为,可以导致事物从一种状态过渡到另一种状态;

4)事物状态变化是有规则的,A状态可以变换到B,B可以变换到C,A却不一定能变换到C;

5)同一种行为,可以将事物从多种状态变成同种状态,但是不能从同种状态变成多种状态。

2、思考实现

参照已有的实现功能 https://github.com/jakesgordon/javascript-state-machine/

实现思路

1、单个页面会有多个UI,每个UI可拥有独立的一个状态机,因此用工厂模式的思路实现

2、状态切换间应该有多个时机传回调函数:onbefore->onleave->on[Event]->on[state]->onenter[State]->onafter[event]

3、留个外部调用的一些方法接口is(判断当前状态是不是此state);can(判断状态十分能转换);cannot;transitions(能转换的状态);

 

 

代码如下

  1 var sm = (function () {
  2     function _StateMachine(config) {
  3         this.state = config.initial||'unknow';
  4         this.events = config.events;
  5         this.callbacks = config.callbacks||{};
  6         this.error = config.error || function(name, from, to, args, error, msg, e) { throw e || msg; }; 
  7         this._hashEvent = {}
  8         this.init();
  9     }
 10     var __PREFIX = ['onbefore','onleave','onenter','onafter'];
 11     _StateMachine.prototype.SYNC = 'sync';
 12     _StateMachine.prototype.init = function () {
 13         this._initHashEvent();
 14         this._buildEvent();
 15     }
 16     _StateMachine.prototype._initHashEvent = function () {
 17         for (var i = this.events.length - 1; i >= 0; i--) {
 18             var e = this.events[i];
 19             if (!this._hashEvent[e.name]) {
 20                 this._hashEvent[e.name] = []
 21             } 
 22             this._hashEvent[e.name].push({
 23                 from:e.from,
 24                 to:e.to,
 25                 name:e.name
 26             })
 27         }
 28     }
 29     _StateMachine.prototype._buildEvent = function () {
 30         var self = this;
 31         for(var i in this._hashEvent){
 32             this[i] = (function (i) {
 33                 return function (arg) {
 34                     var event = this._getEvent(this._hashEvent[i]);
 35                     if (JSON.stringify(event) == '{}') {
 36                         return false;
 37                     }
 38                     this.cacheEvent = event; //用于异步调用
 39                     var fn = event.name;
 40 
 41 
 42                     this.callbacks['onbefore'+fn]&&this.callbacks['onbefore'+fn](this,event.name,this.state,event.state,arg);
 43 
 44                     if(this.callbacks['onbefore'+fn]){
 45                         var beforeValue = this.callbacks['onbefore'+fn](this,event.name,this.state,event.state,arg);
 46                     }
 47                     if(beforeValue == false){
 48                         return false;
 49                     }
 50                     if(this.callbacks['onleave'+this.state]){
 51                         var leaveValue = this.callbacks['onleave'+this.state](this,event.name,this.state,event.state,arg);
 52                     }
 53                     if(leaveValue == false){
 54                         console.log('not transform')
 55                         return false;
 56                     }
 57                     
 58                     if(leaveValue == this.SYNC){
 59                         return false;
 60                     }
 61 
 62                     this._transition(arg);
 63                 }
 64 
 65             })(i);
 66         }
 67     }
 68     _StateMachine.prototype._getEvent = function(hash){
 69         var event={};
 70         for (var i = hash.length - 1; i >= 0; i--) {
 71             var formType = Object.prototype.toString.call(hash[i].from);
 72             if(formType == '[object Array]'){
 73                 if(hash[i].from.indexOf(this.state)>=0){
 74                     event = hash[i];
 75                     break;
 76                 }
 77             }else{
 78                 if(hash[i].from == this.state){
 79                     event = hash[i];
 80                     break;
 81                 }
 82             }
 83         }
 84         return event;
 85     }
 86     _StateMachine.prototype._transition = function (args) {
 87         var event = this.cacheEvent;
 88         var fn = event.name;
 89         this.prevState = this.state;
 90         this.state = event.to;
 91         this.callbacks['on'+event.name]&&this.callbacks['on'+event.name](this,this.prevState,this.state,args);
 92         this.callbacks['on'+this.state]&&this.callbacks['on'+this.state](this,this.prevState,this.state,args);
 93         this.callbacks['onenter'+this.state]&&this.callbacks['onenter'+this.state](this,event.name,this.prevState,event.state,args);
 94         this.callbacks['onafter'+fn]&&this.callbacks['onafter'+fn](this,event.name,this.prevState,event.state,args);
 95         console.log('sm state transform '+ this.prevState+ ' to '+ this.state);
 96 
 97     }
 98     _StateMachine.prototype.is = function (state) {
 99         return this.state == state;
100     }
101     _StateMachine.prototype.can = function (eventName) {
102         var event =  this._getEvent(this._hashEvent[eventName]);
103         return JSON.stringify(event) != '{}';
104     }
105     _StateMachine.prototype.cannot = function (eventName) {
106         var event =  this._getEvent(this._hashEvent[eventName]);
107         return JSON.stringify(event) == '{}';
108     }
109     //以数组的形式返回实例当前状态下能够被触发的行为列表
110     _StateMachine.prototype.transitions = function () {
111         var events = [];
112         for (var e in this._hashEvent) {
113             if(this.can(e)){
114                 events.push(e);
115             }
116         }
117         return events;
118     }
119     return {
120         create:function (config) {
121             return new _StateMachine(config)
122         }
123     }
124 })();

调用 

 1 var fsm2 = sm.create({
 2   initial: 'hungry',
 3   events: [
 4     { name: 'eat',  from: 'hungry', to: 'satisfied' },
 5     { name: 'eat',  from: 'satisfied',to: 'full'      },
 6     { name: 'eat',  from: 'full',to: 'sick'      },
 7     { name: 'rest', from: ['hungry', 'satisfied', 'full', 'sick'], to: 'hungry'    },
 8 ]
 9 });
10 
11 
12 var fsm = sm.create({
13   initial: 'green',
14   events: [
15     { name: 'warn',  from: 'green',  to: 'yellow' },
16     { name: 'panic', from: 'yellow', to: 'red'    },
17     { name: 'calm',  from: 'red',    to: 'yellow' },
18     { name: 'clear', from: 'yellow', to: 'green'  }
19   ],
20   callbacks: {
21     onpanic:  function(event, from, to, msg) { alert('panic! ' + msg);               },
22     onwarn:  function(event, from, to, msg) { alert('warn! ' + msg);               },
23     onclear:  function(event, from, to, msg) { alert('thanks to ' + msg);            },
24     ongreen:  function(event, from, to)      { document.body.className = 'green';    },
25     onyellow: function(event, from, to)      { document.body.className = 'yellow';   },
26     onred:    function(event, from, to)      { document.body.className = 'red';      },
27   }
28 });

 

posted on 2017-05-04 16:56  peace_1  阅读(436)  评论(0编辑  收藏  举报