如何暂停循环刷新API访问令牌后恢复循环?
问题描述
我有一个使用access token的API,通过循环调用该API更新系统中的用户,代码如下:
while (socketerror == 0 && i < usersJson.length) { for (let user of usersJson) { functions.push(() => userFunctions.updateUser(url, accessToken, user)); } }
我在遍历用户进行更新时,access token会过期,需要在调用函数前刷新令牌。我曾考虑在每个用户处理前加入刷新逻辑:
if (new Date.now() < okapiExpiration) { accessToken = function refreshToken(accessToken); }
但由于操作是并行执行的,担心会重复发起令牌刷新请求导致调用报错。之后我考虑在遍历用户前用while循环刷新,但这样每次都会重启整个用户循环。请问如何实现暂停循环、刷新令牌后继续循环?
解决方案
1. 实现令牌刷新互斥锁,避免重复请求
先封装一个获取有效令牌的函数,用Promise作为锁,确保同一时间只有一个刷新请求执行,其他请求等待锁释放后使用新令牌:
let refreshLock = null; // 存储正在进行的刷新操作Promise async function getValidAccessToken() { // 如果已有刷新请求在处理,直接等待其完成 if (refreshLock) { return refreshLock; } // 令牌未过期,直接返回当前令牌 if (Date.now() < okapiExpiration) { return accessToken; } // 发起刷新请求,将Promise赋值给锁 refreshLock = refreshToken(accessToken).then(newToken => { accessToken = newToken; // 更新令牌过期时间,需根据实际刷新接口返回值调整 okapiExpiration = /* 新的过期时间戳 */; refreshLock = null; // 刷新完成后释放锁 return accessToken; }).catch(err => { refreshLock = null; // 刷新失败也要释放锁,避免死锁 throw err; }); return refreshLock; }
2. 改造循环逻辑,实现暂停续更
放弃直接批量生成并行任务的方式,改用async/await控制循环流程,每批(或每个)用户处理前先确保令牌有效,自然实现"暂停-刷新-继续"的效果:
分批并行处理(效率更高)
async function updateUsersInBatches(users, batchSize = 5) { let currentIndex = 0; while (socketerror === 0 && currentIndex < users.length) { // 先获取有效令牌,自动处理刷新逻辑 const validToken = await getValidAccessToken(); // 截取当前批次的用户 const batch = users.slice(currentIndex, currentIndex + batchSize); // 并行处理当前批次的用户更新 await Promise.all( batch.map(user => userFunctions.updateUser(url, validToken, user)) ); currentIndex += batchSize; } } // 调用执行 updateUsersInBatches(usersJson);
串行处理(适合对请求频率有严格限制的场景)
async function updateUsersSerial(users) { let currentIndex = 0; while (socketerror === 0 && currentIndex < users.length) { const validToken = await getValidAccessToken(); // 逐个更新用户,完成后再处理下一个 await userFunctions.updateUser(url, validToken, users[currentIndex]); currentIndex++; } }
3. 兼容原有函数数组的改造方案
如果需要保留原有的"收集函数再执行"的模式,可以在每个更新函数内部先获取有效令牌,利用锁机制避免重复刷新:
while (socketerror == 0 && i < usersJson.length) { for (let user of usersJson) { // 将每个更新操作改为异步函数,先确保令牌有效 functions.push(async () => { const validToken = await getValidAccessToken(); return userFunctions.updateUser(url, validToken, user); }); } } // 执行所有更新操作时用Promise.all // await Promise.all(functions);
内容的提问来源于stack exchange,提问作者Nimrod Yanai
相关产品推荐
相关产品推荐

