35 条回复  ·  3809 次点击
LuckyRock 初学 2025-9-4 10:34:05
假设 token 过期,发了 A 和 B 两个请求,A 请求收到响应之后过期,刷新 token ,刷新 token 的请求先于 B 请求返回,那么此时 isRefreshing 被重置为 false ,然后 B 请求(携带过期 token )返回了,自然就重新走了刷新 token 的逻辑。
Niphor 小成 2025-9-4 10:35:46
根源是 有的请求已经进 finally 了,但是有的请求 response 刚进 interceptor
InDom 初学 2025-9-4 10:36:14
isRefreshing 这个锁不应该是 bool 型 ,而应该是最少三个状态 false, refreshing, true 更好的方案是记录 refreshing 的时间, 再加一个超时的机制. 如果发现 true 就直接使用, 如果是 false 就设置为当前时间并开始更新 token, 如果是 refreshing 的时间, 就检查是否超时(指上一个任务太久未完成) 如果 refreshing 超时, 就自己重新设置 refreshing 时间并开始更新 token 如果 refreshing 未超时, 就设置个随机时间后重新检查 isRefreshing 状态.
supuwoerc 楼主 小成 2025-9-4 10:37:50
@kamilic 醍醐灌顶!我觉得这个分析才解释了真正导致问题的原因,我之前已经怀疑人生了,没想到这种时序导致的问题。
duuu 初学 2025-9-4 10:37:57
你的 isRefreshing 不要做成全局的,改成单个接口自己判断使用。
supuwoerc 楼主 小成 2025-9-4 10:40:20
@LuckyRock @InDom @geelaw 感谢各位大佬,我依然意识到请求时序才是真正导致问题的原因,之前一直盯着判断看,钻牛角尖已经怀疑人生了😂
mrzhiin 初学 2025-9-4 10:41:37
当接口的响应为令牌过期错误的时候,在刷新令牌之前,先判断下接口使用的令牌和当前状态里的令牌是否一致,如果不一致,替换为当前状态里令牌重新再发起请求
LOWINC 小成 2025-9-4 10:42:17
let isRefreshing = false; function request(success, time) { return new Promise((resolve, reject) => { setTimeout(() => { if (success) { resolve({}); } else { reject(); } }, time); }); } Promise.all([ request(false, 100) .then(() => {}) .catch(() => { if (!isRefreshing) { isRefreshing = true; console.log('refresh1:', isRefreshing); setTimeout(() => { isRefreshing = false; }, 100); } }), request(false, 3000) .then(() => {}) .catch(() => { if (!isRefreshing) { isRefreshing = true; console.log('refresh2:', isRefreshing); setTimeout(() => { isRefreshing = false; }, 3000); } }), ]); 可能就是两次 10003
supuwoerc 楼主 小成 2025-9-4 10:43:06
@mrzhiin 嗯嗯,感谢大佬,知道原因的我也想到了这样判断来解决~
canvascat 初学 2025-9-4 10:46:25
不用加锁,复用一下 `refreshTokenPromise`应该就行了 ``` declare const orginalRefreshToken: () => Promise; const refreshToken = () => { refreshToken.current ??= orginalRefreshToken().finally(() => { refreshToken.current = null; }); return refreshToken.current; }; refreshToken.current = null as Promise | null; ```
返回顶部