最近,在JavaScript领域有很多关于有限状态机的讨论。
有一个流行的库叫XState,在GitHub上有超过11000颗星,我最近遇到了它,而且我一直在Twitter和其他地方看到它。这是一个非常酷的项目。
我第一次见到有限状态机和自动机是在20年前的高中,然后我在大学的数字设计课程中再次见到它们。
这个数字设计课程是关于信息编码、布尔代数、组合网络、顺序电路、顺序状态机、算术电路、VHDL等等。
我发现应用于前端工程的有限状态机的主题相当惊人,我回到我的旧书中,看看是否能找到对它们的良好解释。
我确实找到了很多信息,当然,作为教科书,事情被弄得比需要的还要复杂(IMHO)。我喜欢简化事情,我决定为正常人写一点解释,而不是为通过考试而写的理论性或任何东西。你可以在现实世界中应用的东西。
状态机
在研究什么是有限状态机之前,我想首先介绍一下什么是状态机。
世界上到处都是状态机。你可以到处看到它们,但也许你并没有想到它们是这样的。读完本教程后,我相信你会指出现实世界中的物体,对你的朋友说 "那是一个状态机"(取决于你朋友的书呆子水平)。
最流行和最常见的例子是交通灯。
在任何时间点上,交通信号灯都有一个确定的状态。通常情况下,它要么。
- 绿灯亮起,其他两个灯关闭
- 红灯亮起,其他两个灯熄灭
- 黄灯亮起,其他两个灯熄灭

(有些信号灯略有不同,但为了这个例子,我们并不关心)
在状态机的术语中,灯的开启或关闭被称为输出。
这3种情况中的每一种都被称为状态。当交通信号灯收到一个输入时,它将改变状态,通常只是一个固定的计时器,它决定了交通灯应该在多长时间内是绿色、黄色和红色。
在这种情况下,定时器就是系统的输入。有些信号机有一个按钮,行人可以按下,使红色显示为汽车,这将是另一个输入。
在状态机中,状态只有在响应输入时才能改变(我们有一个过渡)。
有限状态机
我们的交通灯状态机被说成是有限的,因为我们有有限数量的状态。
有些系统可能有无限多的状态。
比如世界生态系统模型,或者昆虫的生命。我们不能用有限的状态数来定义它。
但是交通灯呢?那是一个简单的东西,它有3个状态,就像我们上面说的。
我们还可以用很多有限状态机的例子。
- 自动售货机
- 地铁入口旋转门
- 一个供暖系统
- 自动地铁系统
- 自动驾驶汽车系统
- 电梯
但让我们坚持以交通灯为例,它非常简单,我们可以很容易地进行推理。
建立状态机模型
当然,交通灯并不是孤立存在的。那是另一个有限状态机,它包含了多个不同的有限状态机,每个交通灯设备安装在十字路口的每条路的两侧,它们同步工作。

现在不要去想它了。让我们专注于1个单一的交通信号灯。
正如我们上面所说的,我们有3个状态,可以称为绿色、红色、黄色。

我们有一个初始状态。假设我们的交通信号灯开始时,当我们重置它们时,处于green 。

我们有一个计时器,在50秒的绿色状态后,将信号灯移到yellow 。我们有8秒的yellow 状态,然后我们移动到red 状态,并在那里停留32秒,因为那条路是次要的,它应该有更少的绿色时间。在这之后,信号灯又回到了绿色状态,这个循环无限期地继续下去,直到电力停止,信号灯再次获得电力时复位。
总的来说,我们有一个90秒的周期。
这就是我们的建模方式。

我们也可以用状态转换表来建模,它显示了机器目前所处的状态,它收到的输入是什么,下一个状态是什么,以及输出。
| 状态 | 输入 | 下一个状态 | 输出 |
|---|---|---|---|
| 绿色 | 定时器50秒 | 黄灯 | 绿灯亮,其他灯灭 |
| 黄灯 | 定时器8秒 | 红灯 | 黄灯亮,其他灯灭 |
| 红灯 | 定时器32秒 | 绿灯 | 红灯亮,其他灯灭 |
在我们的简单案例中,给定机器的任何状态,我们只有一个逻辑状态可以进入,所以我做的表格和图表是非常非常简单的。
但是,当你的系统开始变得更加复杂时,有这样的图表和分析是非常有帮助的,因为你可以对你的应用程序进行推理,而不是简单地把所有东西都放在脑子里。
一个具有更复杂的状态转换的有限状态机
上面的例子是非常简单的。现在让我们为另一个有限状态机建模。
我们的现实世界是这样的:我们有一个房子,有一扇门,两个按钮和三个灯。
在默认状态下,灯都是关着的。

当你进入房子时,你可以按下2个按钮中的一个,p1 或p2 。当你按下其中任何一个按钮时,l1 灯就会打开。
想象一下,这是入口处的灯,你可以把你的外套脱掉。一旦你完成了,你决定你想进入哪个房间(例如厨房或卧室)。
如果你按下按钮p1 ,l1 关掉,l2 打开。相反,如果你按下按钮p2 ,l1 关闭,l3 打开。
再按一次这两个按钮中的任何一个,p1 或p2 ,当前开启的灯就会关闭,我们就会回到系统的初始状态。
这个有限状态机比第一个稍微复杂一些,因为这一次我们可以根据输入的情况有多条路线。
让我们对此进行建模。
我们基本上有4个状态。
- 没有开灯
l1开启l2开启l3开启

我们有2个输入。
p1p2
如果我们从初始状态开始,并试图模拟所有可能发生的事情,这取决于输入如何随时间被触发,我们最终会得到。

我们可以用这个表来总结一下。
| 状态 | 输入 | 下一个状态 |
|---|---|---|
| 没有开灯 | p1 已按下 | l1 开启 |
| 无灯开启 | p2 已按下 | l1 开启 |
l1 开启 | p1 已按下 | l2 开启 |
l1 开启 | p2 已按下 | l3 开启 |
l2 开启 | p1 已按下 | 无灯开启 |
l2 开启 | p2 已按下 | 不开灯 |
l3 开启 | p1 已按下 | 不开灯 |
l3 开启 | p2 已按下 | 不开灯 |
这是一个简短的解释,但也许它使事情变得 "简单"。