Redux Saga跨saga同步获取token的最优方案及竞态问题解决
Redux Saga 共用token获取函数竞态问题解决方案
问题根因
你当前的实现中,select(isCurrUpdatingToken)读取状态和yield put(startTokenUpdate())更新状态两个操作不是原子操作。应用启动阶段多个saga并发调用getValidAuthToken时,会在第一个请求的isUpdating状态更新到store前,全部读取到isUpdating = false的旧值,最终触发多次重复的token请求。
最优解决方案:基于takeLeading重构刷新逻辑
redux-saga 原生提供的takeLeading副作用,专门处理「仅执行第一个触发的同类型action,后续同类型action在第一个执行完成前全部忽略」的场景,是这类并发去重问题的最优解,改动量小且符合saga设计范式。
改造步骤
- 抽离独立的token刷新处理saga,所有刷新请求通过统一action触发
- 用
takeLeading监听刷新请求action,天然保证同时只有一个刷新请求在运行 - 原有
getValidAuthToken仅做有效性判断,需要刷新时触发action后等待完成通知即可 - 可以直接删除原有
isUpdating相关的状态维护逻辑,减少冗余代码
改造后代码示例
import { takeLeading, take, put, select } from 'redux-saga/effects'; // 统一的token刷新请求action类型,可按项目规范自定义 const REQUEST_VALID_TOKEN = 'auth/REQUEST_VALID_TOKEN'; // 独立的token刷新处理逻辑 function* handleRefreshToken(action: { payload: TokenParams }) { const bufferTimeMS = 10000; // 二次校验token是否有效,避免刚刷新完成又重复请求 const token = yield select(tokenSelector); if (token && Date.now() < token.tokenExpirationTimeStampMs - bufferTimeMS) { return; } // 执行token请求逻辑 const result = yield sdk.getToken(action.payload); const { tokenValue, lifetimeSeconds } = result.token; const tokenExpirationTimeStamp = Date.now() + lifetimeSeconds * 1000; yield put(finishTokenUpdate({ tokenExpirationTimeStampMs: tokenExpirationTimeStamp, tokenValue })); } // 全局监听saga,需要注册到项目根saga中运行 export function* watchTokenRefresh() { // 核心逻辑:同时仅处理第一个触发的刷新请求,后续请求自动忽略 yield takeLeading(REQUEST_VALID_TOKEN, handleRefreshToken); } // 改造后的共用token获取函数 function* getValidAuthToken(params: TokenParams) { const bufferTimeMS = 10000; const token = yield select(tokenSelector); // 现有token有效直接返回 if (token && Date.now() < token.tokenExpirationTimeStampMs - bufferTimeMS) { return token.tokenValue; } // 触发刷新请求,等待刷新完成通知 yield put({ type: REQUEST_VALID_TOKEN, payload: params }); yield take(finishTokenUpdate); const newToken = yield select(tokenSelector); return newToken.tokenValue; }
方案优势
- 从根本上解决了非原子操作导致的竞态问题,不需要自行维护并发控制状态
- 代码逻辑更简洁,减少了手动判断
isUpdating的边界case - 适配所有并发场景,无论是启动阶段的批量请求还是运行中token过期的并发刷新,都能保证仅发起一次token请求
内容的提问来源于stack exchange,提问作者Robert Lemiesz
相关产品推荐
相关产品推荐

