在微信小程序开发中,我们通常会给请求统一加拦截器,处理 token 的过期刷新。
但你可能会遇到一个问题:
接口 A 和接口 B 都需要 token,token 已过期,多接口同时发请求,结果刷新 token 被触发多次,导致请求状态错乱或者失败。
这就是典型的 拦截器踩坑场景。
一、问题场景
假设你的小程序有两个接口:
-
接口 A:获取用户信息
-
接口 B:获取用户订单
两者都需要 accessToken。
如果 token 已经过期:
-
接口 A 判断 token 过期 → 去刷新 token
-
接口 B 几乎同时也判断 token 过期 → 也去刷新 token
结果:
-
同一时间调用了两次刷新接口
-
可能刷新结果互相覆盖
-
请求 A/B 状态可能失败
二、错误做法示例
很多人会在拦截器里写:
javascript
function request(config) {
if (token过期) {
// 每个请求都去刷新
await refreshToken();
}
return wxRequest(config);
}
问题是:多接口同时发起请求,就会重复刷新 token。
三、正确思路:给刷新操作"加锁"
核心思路:
同一时间,只能有一个请求去刷新 token,其他请求等待它完成
实现步骤
-
定义全局变量
refreshTask保存刷新任务(Promise)。 -
当请求发现 token 需要刷新时:
-
如果
refreshTask存在 → 等待它完成 -
如果
refreshTask不存在 → 创建刷新任务
-
-
刷新完成后 → 清空
refreshTask(释放锁)
简单示例
javascript
// 全局锁,保证同一时间只刷新一次
let refreshTask = null;
// 并发安全刷新 token
async function refreshTokenIfNeeded() {
if (refreshTask) return refreshTask; // 已有人在刷新,等待它
refreshTask = (async () => {
await doRefreshToken(); // 实际刷新 token,获取到的token放入缓存中
refreshTask = null; // 完成后释放锁
})();
return refreshTask;
}
// 封装请求,自动加 token
async function wxRequestWithToken(config) {
await refreshTokenIfNeeded(); // 请求前确保 token 有效
return new Promise((resolve, reject) => {
wx.request({
...config,
header: {
...(config.header || {}),
Authorization: `Bearer ${wx.getStorageSync('accessToken')}` // token 放在请求头
},
success: resolve,
fail: reject
});
});
}
四、效果和好处
-
避免重复刷新 → 减少服务器压力
-
避免 token 覆盖 → 请求状态稳定
-
多接口同时发请求 → 自动排队,保证并发安全
五、小结
掌握这个方法后,微信小程序多接口同时请求也不会重复刷新 token,无论是在页面加载时,还是多个组件同时发起请求时,都能保证状态稳定、安全可靠。