@ffort_233/state-stack
v0.1.0
Published
A stack + state machine fusion — handlers peek the stack top, switchStatus drives stack operations, and all side effects are expressed as fixed-form stack primitives (push/pop/run)
Maintainers
Readme
StateStack
来自作者:stateStack 是一个将栈和状态机融合在一起的东西。它的设计有几个关键点。
一是没有用类,整个库实现使用对象表达,但是仍然能够支持多个模块导入导出的静态加强,以一点模块副作用为代价。以及与类相似的声明式体验。
二是支持嵌套,一个 stateStack 可以创建拥有其子 stateStack,并使用显式的 run 在各级 stateStack 和根实例创建者之间进行控制流转移。
三是限制状态机在一轮信息收集-做出行动循环中所能造成的效果,通过让 effect 以固定的形式,栈操作 pop/push 和控制流 run 转移来表达。同时限制 write 写操作,switchStatus 的调用次数。
四是让栈顶元素参与到 statusDispatcher 中影响状态分支,提供了更强的拓展性。
目前项目可能还会缺少一些细节和边界上的优化和处理。欢迎在issue中提出。希望我的项目能够成为你在问题场景中合适的建模工具。
补充:目前项目中的run循环检查到status为null时会返回,这并不符合设计本意。避免使用这种方式返回,而是使用run调用根创造者传入的函数来显式转移控制流。
什么是 StateStack?
StateStack 不是一个普通的状态机库。它适合这样的场景:
你的工作流是"逐层深入、逐层返回"的嵌套结构——比如递归解析、多阶段流水线、嵌套事务。每层做一件事,做完带着结果回上一层。StateStack 把这种"分层推进、逐层返回"的模式固化为三个原语操作:push(入栈)、pop(弹栈)、run(控制权转移)。
什么时候用它?
- 你的流程有"进入子任务 → 子任务完成 → 回到父任务"的天然栈结构
- 你想让每层业务逻辑独立编写,不需要手写状态转移表
- 你想把副作用(入栈、弹栈、调子流程)收束到固定的操作形式,方便做横切(日志、缓存、审计)
什么时候不必要?
- 只有 2-3 个状态的简单标志位 — 一个
switch/case就够了 - 复杂的并发状态、正交区域状态 — XState 等工具更合适
安装
npm install @ffort_233/state-stackimport { createStateStack, refineCreateStateStack } from '@ffort_233/state-stack';快速开始
下面是一个三状态(idle → processing → done)的最小状态机,展示完整的入栈 → 处理 → 弹栈 → 控制权回传,循环终止流程。
import { createStateStack } from '@ffort_233/state-stack';
const ss = createStateStack({
// ── 状态类型声明 ──
state: {
status: ['idle', 'processing', 'done'], // 所有可能的 status 值
resultData: { done: false }, // resultData 字段结构
},
// ── 栈操作定义 ──
peek: (peek) => peek(), // 读取栈顶
push: (push, data) => push(data), // 入栈
pop: (pop, writeResultData) => { // 弹栈 + 写入结果
writeResultData({ done: true });
pop();
},
// ── 状态分发:决定当前执行哪个状态 handler ──
statusDispatcher: (peek, status) => status,
// ── 状态处理函数 ──
idle: (state, peek, api) => {
api.switchStatus('processing', { effect: 'push', param: ['task-001'] });
},
processing: (state, peek, api) => {
api.switchStatus('done', { effect: 'pop' });
},
done: (state, peek, api) => {
api.switchStatus(null, { effect: 'run' }); // {effect:'run'} 控制权回传,循环终止
},
// ── 初始化(createStateStack() 调用时立即执行) ──
init: (state, push) => { state.status = 'idle'; },
});
ss.run();
console.log(ss.readState()); // { status: null, resultData: { done: true } }执行流程:
init → status = 'idle'
→ idle handler: switchStatus('processing', push) → 栈压入 'task-001'
→ processing handler: switchStatus('done', pop) → 弹栈
→ done handler: switchStatus(null, run) → 控制权回传,循环终止文档
| 文档 | 内容 | |------|------| | 使用指南 | 完整的使用示例:peek、statusDispatcher、effect、受限函数、子栈、模块链、真实场景 | | 核心概念 | peek 的本质、statusDispatcher 职责、受限函数设计哲学、effect 语义 | | 模块链(Refinement) | AOP 式函数覆写:为什么叫 refine、单模块/多模块/链式语法、适用场景 | | API 参考 | 完整接口签名、类型定义、限制规则、错误情况 |
源码结构
src/`n├── Stack.ts # 原始栈 + 状态原型(simplest 层)
├── funcTransformer.ts # overwriteChain 函数复合(reduce)
├── closureService.ts # 模块链存储
├── funcTimer.ts # 受限函数计数器包装
├── createStateStack.ts # 入口:模块链 / 实例创建双模式
└── createStateStackCore.ts # 核心工厂:四层作用域 + 运行循环