查看: 2209|回复: 1

JavaScript与有限状态机 by 阮一峰

[复制链接]
  • TA的每日心情
    郁闷
    2024-10-28 10:11
  • 签到天数: 1703 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    发表于 2014-4-1 10:39:33 | 显示全部楼层 |阅读模式
    分享到:
    http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html
    有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物。

    简单说,它有三个特征:
      * 状态总数(state)是有限的。
      * 任一时刻,只处在一种状态之中。
      * 某种条件下,会从一种状态转变(transition)到另一种状态。
    它对JavaScript的意义在于,很多对象可以写成有限状态机。
    举例来说,网页上有一个菜单元素。鼠标悬停的时候,菜单显示;鼠标移开的时候,菜单隐藏。如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变。
    代码可以写成下面这样:

      var 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;
          }
        }

      };

    可以看到,有限状态机的写法,逻辑清晰,表达力强,有利于封装事件。一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法。
    另外,JavaScript语言是一种异步操作特别多的语言,常用的解决方法是指定回调函数,但这样会造成代码结构混乱、难以测试和除错等问题。有限状态机提供了更好的办法:把异步操作与对象的状态改变挂钩,当异步操作结束的时候,发生相应的状态改变,由此再触发其他操作。这要比回调函数、事件监听、发布/订阅等解决方案,在逻辑上更合理,更易于降低代码的复杂度。
    下面介绍一个有限状态机的函数库Javascript Finite State Machine。这个库非常好懂,可以帮助我们加深理解,而且功能一点都不弱。
    该库提供一个全局对象StateMachine,使用该对象的create方法,可以生成有限状态机的实例。

      var fsm = StateMachine.create();

    生成的时候,需要提供一个参数对象,用来描述实例的性质。比如,交通信号灯(红绿灯)可以这样描述:

      var 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' }
        ]

      });

    交通信号灯的初始状态(initial)为green,events属性是触发状态改变的各种事件,比如warn事件使得green状态变成yellow状态,stop事件使得yellow状态变成red状态等等。
    生成实例以后,就可以随时查询当前状态。
    * fsm.current :返回当前状态。
    * fsm.is(s) :返回一个布尔值,表示状态s是否为当前状态。
    * fsm.can(e) :返回一个布尔值,表示事件e是否能在当前状态触发。
    * fsm.cannot(e) :返回一个布尔值,表示事件e是否不能在当前状态触发。
    Javascript Finite State Machine允许为每个事件指定两个回调函数,以warn事件为例:
    * onbeforewarn:在warn事件发生之前触发。
    * onafterwarn(可简写成onwarn) :在warn事件发生之后触发。
    同时,它也允许为每个状态指定两个回调函数,以green状态为例:
    * onleavegreen :在离开green状态时触发。
    * onentergreen(可简写成ongreen) :在进入green状态时触发。
    假定warn事件使得状态从green变为yellow,上面四类回调函数的发生顺序如下:onbeforewarn → onleavegreen → onenteryellow → onafterwarn。
    除了为每个事件和状态单独指定回调函数,还可以为所有的事件和状态指定通用的回调函数。
    * onbeforeevent :任一事件发生之前触发。
    * onleavestate :离开任一状态时触发。
    * onenterstate :进入任一状态时触发。
    * onafterevent :任一事件结束后触发。
    如果事件的回调函数里面有异步操作(比如与服务器进行Ajax通信),这时我们可能希望等到异步操作结束,再发生状态改变。这就要用到transition方法。

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

    上面代码的回调函数里面,有一个异步操作(light.fadeOut)。如果不希望状态立即改变,就要让回调函数返回StateMachine.ASYNC,表示状态暂时不改变;等到异步操作结束,再调用transition方法,使得状态发生改变。
    Javascript Finite State Machine还允许指定错误处理函数,当发生了当前状态不可能发生的事件时自动触发。

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

    比如,当前状态是green,理论上这时只可能发生warn事件。要是这时发生了stop事件,就会触发上面的错误处理函数。
    Javascript Finite State Machine的基本用法就是上面这些,更详细的介绍可以参见它的主页。

    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2016-1-12 22:37
  • 签到天数: 259 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2014-4-1 20:53:18 | 显示全部楼层
    帮顶了,我解除到状态机还是从讲430的一本很好的书里面看到的
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-11-25 20:41 , Processed in 0.128411 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.