zoukankan      html  css  js  c++  java
  • 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 });
  • 相关阅读:
    JNA 简单示例
    WPF中使用VisiFire制作chart报表
    ActiveMQ CMS 开发环境编译
    c# 程序打包发布
    WPF 程序未处理异常 的捕获
    制作简易浏览器
    C#.NET 支持文件拖放
    C/S代码一例
    Delphi 2010 TStreamReader 和TStreamWriter
    Json数据使用及学习方法
  • 原文地址:https://www.cnblogs.com/peace1/p/JavaScriptStateMachine.html
Copyright © 2011-2022 走看看