yield仅提供生成器暂停与恢复机制,不自带撤销/恢复能力;实现同步命令模式的撤回与重做需配合外部状态管理(如命令对象、历史栈),将命令逻辑拆分为可暂停步骤并手动记录每步状态及反向操作。

yield 本身不提供撤销与恢复轨迹能力,它只是暂停和恢复生成器执行的机制。所谓“完美复刻同步命令模式的撤销/恢复”,需配合外部状态管理(如命令对象、历史栈)来实现,yield 只负责让流程可中断、可续跑。
想用 yield 模拟同步命令的「撤回」和「重做」,关键不是 yield 多神奇,而是你如何把命令逻辑拆成可暂停的步骤,并把每一步的输入、输出、副作用记录下来——yield 只是帮你自然地切分这些步骤。
用 yield 把命令变成可暂停的执行序列
同步命令(比如编辑器里的“插入文字”“删除选区”)本质是一次性执行完的操作。要支持撤销,就得让它“能停、能记、能倒带”。yield 让你把一个大操作写成一串小步骤,每次 yield 返回当前状态快照,调用方决定是否继续:
- 每个 yield 点对应一个原子操作(如“光标移动到位置X”“渲染第N帧”)
- yield 返回的是该步完成后的上下文(如 { cursor: 12, text: "hello_" }),不是结果值本身
- 生成器外维护 history 栈:每次 next() 后把返回的状态 push 进栈;undo 就是 pop 并回退到前一个状态
撤销时不能只靠 yield,得靠状态快照 + 反向操作
yield 不会自动保存变量或还原内存。真正实现 undo,必须在每次 yield 前手动捕获关键状态,或者为每步定义对应的反向命令:
- 正向 yield:执行 insert("world") → 更新文本、移动光标 → yield { text, cursor }
- 反向操作:不是“让 yield 倒着走”,而是调用 deleteAt(cursor - 5, 5) → 恢复上一状态
- 推荐方式:每个 yield 步骤封装为 Command 对象,自带 execute() 和 unexecute() 方法,yield 只驱动执行顺序
恢复(redo)依赖可重放的生成器实例
Python 中生成器不可重入,但可以保存生成器对象引用,或重新构造:
- 执行时用 list(gen) 或 for x in gen: ... 收集所有中间状态,后续从某点重放
- 更稳妥做法:把 yield 流程封装进类,用 self.state 记录进度索引,redo 就是 reset index 并重跑对应子序列
- 避免直接 yield 大量中间数据;改用 yield dict(step="insert", pos=10, char="a") 这类轻量指令,由外部解释器执行并记录副作用
别混淆 yield 和事务回滚
yield 不等于数据库 rollback,也不触发自动内存回溯。它只是协作式暂停——暂停后你仍需自己保证状态一致性:
- 不要在 yield 后修改共享对象而不记录变更(比如 yield 之后偷偷改了全局 list)
- 推荐不可变数据结构:每次 yield 都返回新状态对象,旧状态自然保留,undo 就是切换引用
- 如果涉及 I/O(如发 HTTP 请求),yield 不能撤销已发出的请求,只能靠业务层补偿(如调用 cancel 接口或发送逆向请求)











