Frontend Development 5 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Ensuring a Single Token Request Across Multiple API Calls with a repeatOnce Function

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.

frontendJavaScriptconcurrencyAxiostokenPromiselocalStorage
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.