新闻动态

事件通知期间删除监听者为何仍可能收到本轮通知:十二轮事件快照与名单修改·练习11

2026-11-01

一个事件开始通知后,某个回调请求删掉另一个监听者,该监听者本轮是否还会收到通知,取决于遍历与修改规则。先固定名单的方案可以把本轮投递与下一轮名单分开理解。

本篇使用一组可以手算或逐行复查的虚构游戏记录。先读清操作顺序与边界,再看完整过程;它不是某一款现有游戏的隐藏公式,也不要求读者安装工具。

规则与输入

每轮开始复制当前有序监听名单,本轮所有投递按这份快照完成。回调产生的增加或删除请求随后按顺序应用于原名单,新增已有编号无效果,删除不存在编号也无效果。本例忽略回调异常和嵌套事件。

初始名单['A', 'E', 'D'] 每轮修改请求[[['remove', 'B'], ['remove', 'C'], ['add', 'B']], [['remove', 'E'], ['add', 'F'], ['remove', 'E']], [['remove', 'F'], ['remove', 'A'], ['add', 'C']], [['remove', 'A'], ['remove', 'F'], ['add', 'C']], [['add', 'B'], ['remove', 'F'], ['remove', 'C']], [['add', 'C'], ['remove', 'A'], ['remove', 'D']], [['add', 'E'], ['add', 'E'], ['add', 'F']], [['remove', 'C'], ['add', 'E'], ['add', 'A']], [['remove', 'C'], ['remove', 'E'], ['add', 'F']], [['add', 'E'], ['add', 'F'], ['remove', 'A']], [['add', 'D'], ['remove', 'A'], ['remove', 'F']], [['add', 'B'], ['remove', 'F'], ['remove', 'A']]]

逐步计算与结果

第1轮固定投递快照['A', 'E', 'D'],本轮依次修改请求[['remove', 'B'], ['remove', 'C'], ['add', 'B']],处理完后下一轮名单['A', 'E', 'D', 'B']。本轮收件人仍然为['A', 'E', 'D'],既不追加新成员也不撤销已经确定的投递。

第2轮固定投递快照['A', 'E', 'D', 'B'],本轮依次修改请求[['remove', 'E'], ['add', 'F'], ['remove', 'E']],处理完后下一轮名单['A', 'D', 'B', 'F']。本轮收件人仍然为['A', 'E', 'D', 'B'],既不追加新成员也不撤销已经确定的投递。

第3轮固定投递快照['A', 'D', 'B', 'F'],本轮依次修改请求[['remove', 'F'], ['remove', 'A'], ['add', 'C']],处理完后下一轮名单['D', 'B', 'C']。本轮收件人仍然为['A', 'D', 'B', 'F'],既不追加新成员也不撤销已经确定的投递。

第4轮固定投递快照['D', 'B', 'C'],本轮依次修改请求[['remove', 'A'], ['remove', 'F'], ['add', 'C']],处理完后下一轮名单['D', 'B', 'C']。本轮收件人仍然为['D', 'B', 'C'],既不追加新成员也不撤销已经确定的投递。

第5轮固定投递快照['D', 'B', 'C'],本轮依次修改请求[['add', 'B'], ['remove', 'F'], ['remove', 'C']],处理完后下一轮名单['D', 'B']。本轮收件人仍然为['D', 'B', 'C'],既不追加新成员也不撤销已经确定的投递。

第6轮固定投递快照['D', 'B'],本轮依次修改请求[['add', 'C'], ['remove', 'A'], ['remove', 'D']],处理完后下一轮名单['B', 'C']。本轮收件人仍然为['D', 'B'],既不追加新成员也不撤销已经确定的投递。

第7轮固定投递快照['B', 'C'],本轮依次修改请求[['add', 'E'], ['add', 'E'], ['add', 'F']],处理完后下一轮名单['B', 'C', 'E', 'F']。本轮收件人仍然为['B', 'C'],既不追加新成员也不撤销已经确定的投递。

第8轮固定投递快照['B', 'C', 'E', 'F'],本轮依次修改请求[['remove', 'C'], ['add', 'E'], ['add', 'A']],处理完后下一轮名单['B', 'E', 'F', 'A']。本轮收件人仍然为['B', 'C', 'E', 'F'],既不追加新成员也不撤销已经确定的投递。

第9轮固定投递快照['B', 'E', 'F', 'A'],本轮依次修改请求[['remove', 'C'], ['remove', 'E'], ['add', 'F']],处理完后下一轮名单['B', 'F', 'A']。本轮收件人仍然为['B', 'E', 'F', 'A'],既不追加新成员也不撤销已经确定的投递。

第10轮固定投递快照['B', 'F', 'A'],本轮依次修改请求[['add', 'E'], ['add', 'F'], ['remove', 'A']],处理完后下一轮名单['B', 'F', 'E']。本轮收件人仍然为['B', 'F', 'A'],既不追加新成员也不撤销已经确定的投递。

第11轮固定投递快照['B', 'F', 'E'],本轮依次修改请求[['add', 'D'], ['remove', 'A'], ['remove', 'F']],处理完后下一轮名单['B', 'E', 'D']。本轮收件人仍然为['B', 'F', 'E'],既不追加新成员也不撤销已经确定的投递。

第12轮固定投递快照['B', 'E', 'D'],本轮依次修改请求[['add', 'B'], ['remove', 'F'], ['remove', 'A']],处理完后下一轮名单['B', 'E', 'D']。本轮收件人仍然为['B', 'E', 'D'],既不追加新成员也不撤销已经确定的投递。

快照与可变名单分开记录;重复增加和删除缺席编号都不会制造重复成员。

怎样独立复核

将本轮实际收到通知的编号与轮开始名单逐项比较。所有修改只能改变下一轮名单,不能倒过来改写已经固定的投递记录;空名单也允许有外部请求,为下一轮增加监听者。

其他事件系统可以选择即时移除或延迟到更晚阶段,因此不能据本例断言某个真实接口一定如此。先确认采用的规则,再排查多收到一次或晚收到一次的现象,尤其注意新增监听者不会补收本轮事件。

练习时可以只改变一个输入,再预测哪一步会最先改变。保留中间记录,检查空状态、相等数值和刚好落在边界的情况。最终结果碰巧相同,并不能证明两个过程等价;反过来,采用不同规则得出不同结果,也不应立即视作程序故障。

准备好开始吧,它既快速又简单。