Generator 配合 yield 并非直接实现撤销重做,而是与命令模式+双栈协同:Generator 组织多步骤流程为可暂停/回溯序列,命令对象封装 execute/undo 逻辑,UndoManager 管理双栈执行原子操作。

用 Generator 配合 yield 实现表单多步骤撤销重做,不是最直接的方案——它本身不管理状态,也不自动记录操作历史。真正起作用的是命令模式 + 双栈结构;Generator 的价值在于把多步骤表单流程组织成可暂停、可回溯的执行序列,再与 UndoManager 协同,让“步骤跳转”和“状态回退”逻辑更清晰、更可控。
表单步骤本质是状态变更序列
一个 5 步的注册表单(姓名 → 邮箱 → 密码 → 头像 → 协议),每步提交都是一次状态变更。如果直接用对象深拷贝存全量快照,内存开销大、diff 困难;而 Generator 能把整个流程抽象为“可逐帧执行/回退”的迭代器:
- 每调用一次
next(),就推进到下一步(比如校验+保存当前字段) - 每调用一次
throw()或自定义back(),就能触发上一步的 undo 逻辑 - yield 出的不是值,而是封装了 execute/undo 的命令对象,而非原始数据
Generator + 命令对象:让步骤变成“可撤销的动作”
不要 yield 字符串或纯数据,而是 yield 命令实例。例如:
function* formFlow() {
yield new SetFieldCommand(form, 'name', '');
yield new SetFieldCommand(form, 'email', '');
yield new SetFieldCommand(form, 'password', '');
yield new UploadImageCommand(form, null);
yield new ToggleAgreementCommand(form, false);
}每个命令内部保存前后状态(如 oldEmail / newEmail),并实现 execute() 和 undo()。Generator 不负责执行,只提供顺序契约;真正执行交给 UndoManager:
立即学习“Java免费学习笔记(深入)”;
-
manager.execute(command)→ 执行 + 入 undo 栈 -
manager.undo()→ 弹出命令、调用 undo、入 redo 栈 - Generator 迭代器仅用于驱动步骤顺序,不持有状态,也不参与栈管理
关键协同点:用 next()/return() 控制步骤导航
用户点击「上一步」时,不直接 pop 栈,而是调用 Generator 的 return() 或维护游标索引,再触发对应命令的 undo:
- 初始化:
iterator = formFlow(); currentStep = 0; - 下一步:
iterator.next(); currentStep++;→ 同时manager.execute(cmd) - 上一步:
currentStep--;→manager.undo()(自动还原上一命令) - 跳转到第 3 步:
while (currentStep > 2) manager.undo();,再用iterator重新生成前 3 步命令(或缓存命令数组避免重复创建)
这样既保留了 Generator 的线性表达力,又没绕过命令模式的可靠性——所有状态变更仍由 Command 对象承载,UndoManager 保证原子性和一致性。
注意事项:别让 Generator 承担不该管的事
Generator 是流程编排器,不是状态管理者。容易踩的坑包括:
- ❌ 在 yield 表达式里直接修改表单 DOM 或发起 API 请求 —— 这会让 undo 失效(副作用无法回滚)
- ❌ 把整个表单 state 作为 yield 值传递 —— 丢失命令的封装性,无法精准 undo
- ✅ 正确做法:命令对象内聚操作逻辑(含 API 调用补偿),Generator 只协调「何时执行哪个命令」
- ✅ 异步步骤(如头像上传)需在命令中封装 Promise,并设计 cancel/rollback 机制,而非依赖 yield 暂停来“等响应”
本质上,Generator 让你把“用户走几步、退回几步”这个交互意图,映射成清晰的代码结构;而命令模式 + 双栈,才是让每一步真正可逆的底层保障。两者配合,不是替代关系,而是分工协作。


















