取消需真正中止而非仅隐藏
别名: 假取消 · abort 而非 dismiss · hidden-in-flight
概念解释
点取消之后,等待皮可以立刻消失,请求却还在飞,服务器还在写。这是假取消:界面离开了等待,工作没有中止。人的模型是「这件事停了」;系统的模型是「展示停了」。两边一旦分叉,后续全是幽灵——重复提交撞上仍在进行的那一次、以为没付款其实扣了款、回来看见一份自己取消过的结果。取消的语义是 abort:停传输、停处理、停副作用,而不只是 dismiss。
机制
等待界面是人对这次任务的唯一观测孔。关掉观测孔,不等于关掉任务。HTTP 请求被丢在一边、WebSocket 仍推、服务端作业已入队,都是观测孔关闭后任务还活着的典型路径。客户端若只 setState(idle) 而不 abort(),连本地都没停。人按「停了」去规划下一步:再点一次、去改参数、离开页面。活着的那一次仍按旧参数写,于是出现双写、写在已离开的上下文里、或在人已经开始做相反操作时完成。
真中止要穿过三层:界面不再等待、客户端不再处理响应、对端不再继续这份工作。少一层,假取消就从那一层漏出来。做不到第三层时,不能假装做了前两层就等于取消——必须改口,把操作说成「停止显示」,并指向仍能查询的任务。那已经是在承认 abort 失败,不是在履行取消。
边界
只读的取数,对端不停有时可以容忍(浪费一次计算),但响应仍必须丢弃,不能在取消之后把结果写进界面或缓存,否则人会看见一份「已取消却出现」的内容。写操作、付款、删除,对端不停就是事故,不能靠丢弃响应来补。取消到对端的信号自己也会失败,需要幂等:对端即使晚收到 abort,再次执行同一份工作也应是无操作。本地已经完成、只差最后一帧展示时,abort 可能赛不过完成事件,这时应按完成来,不要在完成之后再显示一份取消成功。
怎么落地
- 取消路径同时:收起等待、abort 客户端请求、向对端发中止;写操作必须等到对端确认停,或明确降级为「已停止显示,任务可能仍在进行」。
- 取消之后到达的响应一律丢弃,禁止写入界面和缓存。
- 无法 abort 对端时,不要把按钮叫「取消」,改口并给出任务查询处。
- 验证:点取消后立刻在服务端或代理上看该请求。仍在写或稍后仍把结果推回界面,就是假取消。再点一次同一操作,不应变成两次提交。