多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

异步接口的状态更新:如何避免旧响应覆盖新任务

异步接口的状态更新:如何避免旧响应覆盖新任务 先看一个常见的时序问题用户打开任务列表界面发出第一次查询。随后用户切换筛选条件界面发出第二次查询。第二次查询先返回页面显示了正确的新列表第一次查询稍后返回如果代码无条件赋值就会把旧列表重新显示出来。这个问题并不要求服务器出错也不需要极端网络环境。只要两个请求的耗时不同就可能出现。把查询间隔调长只能降低出现概率不能改变响应乱序的事实。请求序号保护当前视图一种简单方式是在发起请求前增加序号响应返回后比较当前序号。只有仍然对应最新请求的结果才允许修改界面。序号应按需要隔离不同组件或独立查询不应共享同一个全局序号否则一个页面刷新可能误取消另一个页面的结果。伪代码可以表达为先令 currentSequence 加一保存到 localSequence等待查询完成若 localSequence 不等于 currentSequence 就丢弃结果。错误提示与 loading 状态也需要同样的判断否则旧请求的失败提示仍可能覆盖新请求的成功界面。取消请求和拒绝旧结果不是一回事在支持取消的客户端中可以在新查询发起时取消旧查询以减少无用的传输和解析。但取消信号可能发出得太晚服务端也可能已经完成处理。因此界面仍应保留响应序号校验。对于产生业务副作用的请求更不能把取消请求理解为取消业务。关闭页面或中止连接并不代表服务器回滚。创建、支付、发布等操作应使用各自的业务协议确认结果而不能沿用普通列表查询的处理方式。任务重试需要更稳定的身份如果任务允许重试只有任务 ID 通常还不够。第一次执行的迟到响应可能在第二次执行开始后返回。可以将任务 ID 与 attempt_id 或执行版本一起传递客户端和服务器都核对该响应是否属于当前尝试。这里的版本不是显示用的“第几次”文字而是状态更新的约束。数据库更新可以要求当前版本与请求携带版本一致更新行数为零时说明请求已过期或状态被其他执行者推进调用方应读取最新记录而不是强行覆盖。状态本身也有不可逆的边界同一次执行中已经确认完成的结果不应被较旧的处理中快照覆盖。可以在合并数据时同时考虑执行身份、状态版本和终态证据。单看 updated_at 容易受到时钟差异、接口缓存与字段格式影响最好由服务端提供稳定的递增版本或受约束的状态转换。这不意味着所有终态永远不能改变。比如业务允许撤销、审核后驳回就应把这些动作定义为明确事件并记录新的版本。不能把一条过期轮询响应和一次真实撤销混在同一条覆盖规则中。让测试覆盖响应顺序可以在测试中准备两个可手动完成的 Promise先发出旧查询再发出新查询先完成新查询最后完成旧查询。断言页面仍保留新结果并检查旧查询的异常不会改变提示或 loading。还应测试组件卸载后响应返回、筛选条件连续变化以及任务重试后上一轮请求才完成。这些用例不需要真实弱网也不依赖随机延迟因而更容易稳定复现。真实网络测试可作为补充用来发现连接、缓存或会话相关问题。把用户看到的状态变成可信结果可靠的状态展示来自三个层次客户端拒绝过期视图响应业务执行使用明确身份服务端约束状态更新顺序。分别建立这些边界比在页面上增加越来越多的刷新按钮更容易维护。
返回列表