当你在使用Vuex、Redux、Pinia的时候,别人已经开始手写状态机了(上)

10673

大家好,我是每天坚持一点点的铁蛋儿,一个爱分享前端的老body。这篇是分享手写状态机,大家务必点赞收藏保存一下慢慢看,你们的关注是我更新的动力。

相关学习资源

本系列有配套视频,大家学习同时千万不要忘了三连 + 关注 + 分享,有道是喝水不忘挖井人~

当我们在学习Vuex、Redux、Pinia的时候 “状态机”这个词时不时的会出现一下。虽然咱们不是计算机科学家,但是对于这样的计算机理论方面的词汇还是要有基本理解的。

1. 定义

状态机的基本定义。一句话:

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。

状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。 1

自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。

状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。

这样状态机的基本定义我们就介绍完毕了。

重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

四大概念

下面来给出状态机的四大概念。

第一个是 State ,状态。一个状态机至少要包含两个状态。例如上面自动门的例子,有 open 和 closed 两个状态。

第二个是 Event ,事件。事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。

第三个是 Action ,动作。事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个 Action 一般就对应一个函数。

第四个是 Transition ,变换。也就是从一个状态变化为另一个状态。例如“开门过程”就是一个变换

好了废话说完了,直接上代码。

2. 实现状态的依赖收集

为了方便流的管理使用Rx.js管理,不熟悉的Rx.js的小伙伴可以去看下官网

  1. 实现状态收集
 import { Observable } from 'rxjs'
 type ObservableFunction = (data: any) => void
 export class Emiter<Topic extends string | number>{
   private observers: Map<Topic, ObservableFunction[]>
   constructor() {
     this.observers = new Map()
   }
   private addObserverFunction(topic: Topic, fn: ObservableFunction) {
     if (!this.observers.has(topic)) {
       this.observers.set(topic, [])
     }
     this.observers.get(topic)?.push(fn)
   }
   // 收集
   on(topic: Topic | Topic[]): Observable<any> {
     return new Observable<any>((observer) => {
       if (Array.isArray(topic)) {
         topic.forEach(t => {
           this.addObserverFunction(t, (data) => {
             observer.next(data)
           })
         })
       } else {
         this.addObserverFunction(topic, (data) => {
           observer.next(data)
         })
       }
     })
   }
   // 发射
   emit(topic: Topic, data?: any) {
     this.observers.get(topic)?.forEach(fn => {
       fn(data)
     })
   }
 }
  1. 使用
 // 用例
 enum Topics {
   A,
   B,
   C
 }
 ​
 const emitter = new Emiter<Topics>()
 ​
 emitter.on(Topics.B).subscribe((data) => {
   console.log('a', data)
 })
 ​
 emitter.emit(Topics.B, '我是传过来的铁蛋儿')
 ​

3.未完待续。。。

我是每天坚持进步一点点的铁蛋儿,如果你对技术有像一素的偏执,欢迎vx:15910703837一起学习一起进步。