JavaScript与有限状态机

开发 前端
有限状态机是一个非常有用的模型,可以模拟世界上大部分事物。它对JavaScript的意义在于,很多对象可以写成有限状态机。

有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物。

简单说,它有三个特征:

  * 状态总数(state)是有限的。
  * 任一时刻,只处在一种状态之中。
  * 某种条件下,会从一种状态转变(transition)到另一种状态。

它对JavaScript的意义在于,很多对象可以写成有限状态机。

举例来说,网页上有一个菜单元素。鼠标悬停的时候,菜单显示;鼠标移开的时候,菜单隐藏。如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变。

代码可以写成下面这样:

  1.  
  2.   var menu = {  
  3.         
  4.     // 当前状态  
  5.     currentState: 'hide',  
  6.     
  7.     // 绑定事件  
  8.     initialize: function() {  
  9.       var self = this;  
  10.       self.on("hover", self.transition);  
  11.     },  
  12.     
  13.     // 状态转换  
  14.     transition: function(event){  
  15.       switch(this.currentState) {  
  16.         case "hide":  
  17.           this.currentState = 'show';  
  18.           doSomething();  
  19.           break;  
  20.         case "show":  
  21.           this.currentState = 'hide';  
  22.           doSomething();  
  23.           break;  
  24.         default:  
  25.           console.log('Invalid State!');  
  26.           break;  
  27.       }  
  28.     }  
  29.     
  30.   };  
  31.     

可以看到,有限状态机的写法,逻辑清晰,表达力强,有利于封装事件。一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法。

另外,JavaScript语言是一种异步操作特别多的语言,常用的解决方法是指定回调函数,但这样会造成代码结构混乱、难以测试和除错等问题。有限状态机提供了更好的办法:把异步操作与对象的状态改变挂钩,当异步操作结束的时候,发生相应的状态改变,由此再触发其他操作。这要比回调函数、事件监听、发布/订阅等解决方案,在逻辑上更合理,更易于降低代码的复杂度。

下面介绍一个有限状态机的函数库Javascript Finite State Machine。这个库非常好懂,可以帮助我们加深理解,而且功能一点都不弱。

该库提供一个全局对象StateMachine,使用该对象的create方法,可以生成有限状态机的实例。

  1. var fsm = StateMachine.create(); 

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

  1. var fsm = StateMachine.create({  
  2.     
  3.     initial: 'green',  
  4.     
  5.     events: [  
  6.       { name: 'warn',  from: 'green',  to: 'yellow' },  
  7.       { name: 'stop', from: 'yellow', to: 'red' },  
  8.       { name: 'ready',  from: 'red',    to: 'yellow' },  
  9.       { name: 'go', from: 'yellow', to: 'green' }  
  10.     ]  
  11.     
  12.   });  
  13.     

交通信号灯的初始状态(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方法。

  1. fsm.onleavegreen = function(){  
  2.     light.fadeOut('slow'function() {  
  3.       fsm.transition();  
  4.     });  
  5.     return StateMachine.ASYNC;  
  6.   };  
  7.     

上面代码的回调函数里面,有一个异步操作(light.fadeOut)。如果不希望状态立即改变,就要让回调函数返回StateMachine.ASYNC,表示状态暂时不改变;等到异步操作结束,再调用transition方法,使得状态发生改变。

Javascript Finite State Machine还允许指定错误处理函数,当发生了当前状态不可能发生的事件时自动触发。

  1. var fsm = StateMachine.create({  
  2.     // ...  
  3.     error: function(eventName, from, to, args, errorCode, errorMessage) {  
  4.       return 'event ' + eventName + ': ' + errorMessage;  
  5.     },  
  6.     // ...   
  7.   });  
  8.     

比如,当前状态是green,理论上这时只可能发生warn事件。要是这时发生了stop事件,就会触发上面的错误处理函数。

Javascript Finite State Machine的基本用法就是上面这些,更详细的介绍可以参见它的主页

原文链接:http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html

责任编辑:林师授 来源: 阮一峰的网络日志
相关推荐

2021-04-29 09:31:05

前端开发技术

2021-09-07 06:40:26

状态机识别地址

2022-03-06 19:57:50

状态机easyfsm项目

2014-05-21 11:09:56

前端有限状态机

2023-04-12 07:14:31

Spring应用业务

2023-03-06 07:35:30

状态机工具订单状态

2021-07-08 09:15:20

单片机编程状态机编程语言

2010-06-18 12:38:38

UML状态机视图

2010-06-18 13:25:44

UML状态机视图

2024-10-10 17:46:06

2014-10-24 13:48:05

2014WOT深圳手游

2021-12-28 08:24:18

函数指针有限状态机编程

2010-07-08 13:03:31

UML状态机图

2011-06-24 16:09:24

Qt 动画 状态机

2020-03-27 10:50:29

DSL 状态机工具

2010-07-12 15:00:56

UML状态机视图

2010-06-12 11:11:55

UML应用

2021-08-19 09:00:00

微服务开发架构

2020-12-02 13:33:58

函数指针编程语言

2010-02-24 09:32:38

Visual Stud
点赞
收藏

51CTO技术栈公众号