周三下午三点,你的用户正在填写一份重要表单。三个API请求同时发出,token恰好过期。下一秒,页面跳转登录页,数据全部丢失。这不是用户操作失误,是你的刷新逻辑在并发场景下崩溃了。
我见过太多这样的生产事故。问题从来不是"会不会发生",而是"什么时候发生"。当三个请求同时触发401,各自独立去刷新token,后端出于安全考虑只会承认其中一个,其余全部失效。于是你陷入死循环:刷新→失败→再刷新→再失败。用户毫无感知,直到客服工单爆炸。
打开网易新闻 查看精彩图片
常见的两种解法都有硬伤。Redux中间件配重试队列?代码膨胀,心智负担重。localStorage检查?两个请求同时读到过期token,照样撞车。我在CitizenApp里用的方案是React Context配合AbortController,原生依赖,逻辑干净,扛住了真实并发流量。
打开网易新闻 查看精彩图片
核心思路是把token刷新变成单例任务。用useRef挂一个refreshPromise,所有请求共享同一个刷新过程。第一个请求发现过期,启动刷新,后续请求检测到promise存在,直接await它。AbortController负责取消可能冲突的前一次刷新,避免竞态。
代码结构分三层:Context管状态,getValidToken暴露给调用方,performRefresh内部串行化。sessionStorage比localStorage安全些,至少不会被第三方脚本随手读走。关键是那个refreshPromiseRef——它让"刷新中"本身成为一种状态,天然排队。
打开网易新闻 查看精彩图片
这个模式省掉了多少麻烦?没有外部状态库,没有复杂的重试逻辑,几十行代码覆盖并发场景。React 19的改进让Context性能不再是借口,是时候重新考虑这套原生方案了。
热门跟贴