[路飞]_有限状态机的前端实现

1,913 阅读3分钟

「这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

学习有限状态机之前我们要先了解,什么是有限状态机。

有限状态机是一个非常有用的模型,可以模拟世界上大部分事物的一个数学模型。在任何时候都处于有限数量的状态之一。响应某些外部输入,就可以从一个状态转换到另一个状态。

有限状态机有三个特征:

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

而在JS中,新建一个对象,用这个对象的属性来模拟元素的状态,用这个对象的方法模拟元素在不同状态的转变。那么这个对象就是一个有限状态机。是否能用状态机描述,取决于当前状态确定,有限个状态,响应事件,在不同状态间有规律的转变。Promise就是一个有限状态机。

有限状态机需要满足所需状态确定,有事件触发转变状态,总状态有限且转变有规律。例如开关,下拉菜单,游戏中生死状态等。

今天我们通过一个开源项目来学习状态机。

javascript-state-machine

有限状态机库。

In a browser:

<script src='state-machine.js'></script>

after downloading the source or the minified version

Using npm:

npm install --save-dev javascript-state-machine

In Node.js:

var StateMachine = require('javascript-state-machine');

A state machine can be constructed using:

var fsm = new StateMachine({
    init: 'solid',
    transitions: [
      { name: 'melt',     from: 'solid',  to: 'liquid' },
      { name: 'freeze',   from: 'liquid', to: 'solid'  },
      { name: 'vaporize', from: 'liquid', to: 'gas'    },
      { name: 'condense', from: 'gas',    to: 'liquid' }
    ],
    methods: {
      onMelt:     function() { console.log('I melted')    },
      onFreeze:   function() { console.log('I froze')     },
      onVaporize: function() { console.log('I vaporized') },
      onCondense: function() { console.log('I condensed') }
    }
  });

上面是作者给出的示例。

今天我们用红绿灯来学习这个项目。

let StateMachine = require('javascript-state-machine')

var fsm = new StateMachine({
  init: 'red',
  transitions: [
		{ name: 'through', from: 'red', to: 'green' },
    { name: 'slow', from: 'green', to: 'yellow' },
    { name: 'stop', from: 'yellow', to: 'red' }
  ],
  methods: {
    onThrough: function () { console.log('绿灯请通行') },
    onSlow: function () { console.log('黄灯请注意') },
    onStop: function () { console.log('红灯请等待') },
  }
})

.. 它创建具有当前状态属性的对象:

init:表示初始状态值,咱们这个实例中的初始状态值是红灯。

如果初始值不存在则会报错

通过fsm.state可以获取当前的状态值

console.log(fsm.state) // red

... 转换到其他状态的方法:

通过调取name可以进行转变

console.log(fsm.state); // red
fsm.through()
console.log(fsm.state); // green

但是,如果你打印的时候会发现,在red和green中间会打印“绿灯请通行”,这是因为在进行转变的时候,调用了onThrough,on,要记住name首字母大写。

但是如果我们当前状态值为red,我们必须按顺序进行下一个状态

console.log(fsm.state); // red
fsm.slow()
// 会报错
throw new Exception("transition is invalid in current state", transition, from, to, this.state);
    ^
{
  message: 'transition is invalid in current state',
  transition: 'slow',
  from: 'red',
  to: undefined,
  current: 'red'
}

默认情况下,如果尝试触发当前状态下不允许的转换,状态机将引发异常。如果希望自己处理问题,可以定义自定义onInvalidTransition处理程序:

methods:{
	onInvalidTransition: function (transition, from, to) {
      console.error("transition not allowed from that state");
   },
}

console.log(fsm.state); // red
fsm.slow()
// transition not allowed from that state

StateMachine也提供了一下的帮助器方法:

  • fsm.is(s) - 如果状态s是当前状态,则返回true

  • fsm.can(t) - 如果从当前状态可以发生转换t,则返回true

  • fsm.cannot(t) - 如果从当前状态无法发生转换t,则返回true

  • fsm.transitions() - 返回当前状态允许的转换列表

  • fsm.allTransitions() - 返回所有可能转换的列表

  • fsm.allStates() - 返回所有可能状态的列表

    console.log(fsm.is('red')); // true console.log(fsm.can('through')); // tr console.log(fsm.cannot('slow')); // true console.log(fsm.transitions()); // [ 'through' ] console.log(fsm.allTransitions()); // [ 'init', 'through', 'slow', 'stop' ] console.log(fsm.allStates()); // [ 'none', 'red', 'green', 'yellow' ]

这就是这个库最基本的功能,当然它还可以有其他扩展功能:

今天就先写到这个,下次我们再来一起探讨一些进阶功能。