hello大家好,我是前端小嘎
大家有没有做过流程管理,比说做抢火车票抢红包,或者多弹窗这样的场景,如果用if else来管理,那么最后就是一团乱麻,而如果我们用有限状态机来管理就非常的简洁明了了。
什么是有限状态机
有限状态机 (Finite State Machine, FSM),是一种数学计算模型 。它被用来描述一个系统在任何给定时间点都只能处于有限个状态 中的一个 ,并且能够根据输入 或事件 从一个状态转换到另一个状态。
有限状态机的核心要素
一个典型的有限状态机由以下几个核心部分组成:
- 状态 (States) :系统可能存在的有限数量的不同情况或模式。例如,一个交通灯有"红灯"、"黄灯"和"绿灯"三种状态。
- 初始状态 (Initial State) :系统在开始时所处的特定状态。
- 输入/事件 (Inputs/Events) :导致状态机从一个状态转换到另一个状态的触发条件。这些可以是外部信号、用户操作或内部条件。例如,交通灯的输入可能是定时器达到某个值,或者车辆传感器检测到有车。
- 转换 :从一个状态到另一个状态的规则或路径。每次转换都会由一个特定的输入或事件触发。例如,从"红灯"到"绿灯"的转换。
- 输出:某些FSM在状态转换或进入某个状态时会产生相应的输出。例如,一个自动售货机在完成交易后可能会输出商品。
解决一个抢红包的场景
分析问题
流程如下 :判断红包是否存在,若存在点击红包=>出现红包弹窗=>点击领取=>出现结果弹窗=>点击关闭弹窗=>回归初始状态,继续监听新红包
先写一个State的抽象类
所有状态必须实现3个方法
onEnd
状态离开时执行
onEnter:
状态进入时执行
onUpdate:
状态激活时一直调用
js
import StateMachine from "./StateMachine";
abstract class AbstractState {
abstract name: StateEnum;
onEnd() {
}
onEnter() {
}
onUpdate() {
}
}
export enum StateEnum {
INIT = "init",
CHECK = "check",
PAY = "pay",
CLAIM = "claim",
SEARCH = "search",
}
export default AbstractState;
接着定义初始状态
这里在查找到红包就可以进入claim 状态了
js
import { findObjectsThen, findPic } from "../utils/findPic";
import State, { StateEnum } from "../State";
import StateMachine from "../StateMachine";
class InitState extends State {
name = StateEnum.INIT;
async onUpdate() {
super.onUpdate();
findObjectsThen([红包])], (UiObject) => {
const comp = UiObject.bounds();
//图像匹配
let pos = findPic("", [comp.left, comp.top, comp.right - comp.left, comp.bottom - comp.top]);
if (pos) {
let res = click(pos.x, pos.y);
// 发现红包后跳到下一个状态
res && StateMachine.pushState(StateEnum.CLAIM);
} else {
//没发现红包就等会
sleep(60 * 1000);
}
});
}
}
export default new InitState();
关键来了
我们需要弄一个状态管理器,在管理器内部保存这当前状态,还有跳转到下一阶段的静态方法,当跳转时会执行前一个状态的onEnd,后一个状态的onEnter
js
import State, { StateEnum } from "./State";
class StateMachine {
public currentState: State | null;
map: Map<StateEnum, State>;
constructor() {
this.currentState = null;
this.map = new Map();
}
pushState(name: StateEnum) {
if (this.map.has(name)) {
let newState = this.map.get(name) as State;
this.currentState?.onEnd();
this.currentState = newState;
this.currentState.onEnter();
}
}
}
export default new StateMachine();
然后我们在main方法中组装
js
function main() {
if (!requestScreenCapture()) {
toast("请求截图失败");
exit();
}
machine.init([InitState, ClaimState, PayState, CheckState], StateEnum.INIT);
while (1) {
sleep(2000);
machine.currentState?.onUpdate();
sleep(2000);
}
}