Ensuring a Single Token Request Across Multiple API Calls with a repeatOnce Function
This article explains how to prevent multiple simultaneous token requests in a web application by using a custom repeatOnce function that caches the token in localStorage and coordinates pending calls through an event emitter, ensuring only the first request fetches the token while others wait for its result.
When a page loads and several API calls are triggered simultaneously, each call may attempt to obtain an authentication token, leading to redundant requests; the desired behavior is that only the first request fetches the token, caches it, and all subsequent calls use the cached token.
The solution is to create a repeatOnce function that wraps the token‑fetching logic. It first checks localStorage for a cached token; if found, it resolves immediately. If not, it uses a static count variable to determine whether the current call is the first one. The first call invokes the provided getResult function, stores the result, resolves the promise, and emits an ok event with the token. Subsequent calls increment the counter and subscribe to the same ok event, resolving only when the first request completes.
function repeatOnce (getResult, local_name) {
return new Promise(async (resolve) => {
const local_result = localStorage.getItem(local_name)
if (local_result) {
console.log('%c [ 存在 ]-1166', 'font-size:13px; background:pink; color:#bf2c9f;')
resolve(local_result)
} else {
if (!repeatOnce.count) {
repeatOnce.count = 0
repeatOnce.count++
console.log('%c [ 刚进入应用页面,缓存结果不存在,需要请求并缓存到本地 ]-1169', 'font-size:13px; background:pink; color:#bf2c9f;')
const request_result = await getResult()
localStorage.setItem(local_name, request_result)
resolve(request_result)
repeatOnce.emitter.emit('ok', request_result)
} else {
repeatOnce.count++
console.log('%c [ repeatOnce.count 第' + repeatOnce.count + '次请求等待返回结果]-64', 'font-size:13px; background:pink; color:#bf2c9f;', repeatOnce.count)
repeatOnce.emitter.on('ok', (result) => {
repeatOnce.count = 0
resolve(result)
})
}
}
})
}
repeatOnce.emitter = {
_events: {},
on(eventName, callback) {
if (!this._events[eventName]) {
this._events[eventName] = [callback]
} else {
this._events[eventName].push(callback)
}
},
emit(eventName, ...args) {
this._events[eventName]?.forEach(event => event(...args))
}
}The count variable tracks whether the token request has already been initiated, while the emitter object provides a simple publish‑subscribe mechanism to notify all waiting calls once the token is available.
Example usage:
// Simulated token request
function getToken() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是请求的结果数据 - token')
}, 1000)
})
}
// Bind repeatOnce to the token function
const token = repeatOnce.bind(null, getToken, 'Access_Token')
// Three page methods that all need the token
async function request1() {
const res = await token()
console.log('%c [ request1 ]-1226', 'font-size:13px; background:pink; color:#bf2c9f;', res)
}
async function request2() {
const res = await token()
console.log('%c [ request2 ]-1226', 'font-size:13px; background:pink; color:#bf2c9f;', res)
}
async function request3() {
const res = await token()
console.log('%c [ request3 ]-1226', 'font-size:13px; background:pink; color:#bf2c9f;', res)
}
// Trigger the three requests; only one token request occurs
request1()
request2()
request3()In many projects this pattern appears frequently, and the repeatOnce approach helps ensure that token acquisition is performed only once while all pending requests receive the same result.
Feel free to leave comments or share alternative ideas; discussion is welcome.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.