有限状态机【Javascript Finite State Machine (v2.3.5)】
## 什么是有限状态机
有限状态机(Finite-state machine)可以模拟世界上大部分事物。
很多对象可以写成有限状态机,或者说有限状态机可以描述很多对象。
比如交通灯,有红、绿、黄三个灯,红灯停,绿灯行,黄灯等一等,
(停+行+等)这就是三个状态,状态个数是有限的,
在同一时刻,交通灯只会处于一种状态之中,
从一种状态转换到另一种状态时有规律:绿→黄→红→绿,
状态机的设计,目的就是为了避免大量状态的判断带来的复杂性,消除庞大的条件分支语句,因为大量的分支判断会使得程序难以修改和扩展。
简单说,它有三个特征:
1. 状态总数(state)是有限的。
2. 任一时刻,只处在一种状态之中。
3. 某种条件下,会从一种状态转变(transition)到另一种状态。
举例来说,网页上有一个菜单元素。鼠标悬停的时候,菜单显示;鼠标移开的时候,菜单隐藏。
如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变。
代码可以写成下面这样:
```
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状态机
### 下载
[state-machine.js](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.js)
[state-machine.min.js](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.min.js)
### 使用
下面介绍一个有限状态机的函数库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: 'go', from: 'red', to: 'green' }
]
});
```
这段代码会为每个事件创建方法:
fsm.warn();
fsm.stop();
fsm.go();
交通信号灯的初始状态(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;
},
// ...
});
```
Github:[https://github.com/jakesgordon/javascript-state-machine/](https://github.com/jakesgordon/javascript-state-machine/)