Home > Blockchain >  JavaScript Stall Parallel Function Calls
JavaScript Stall Parallel Function Calls

Time:03-04

I have create a TokenService class with the purpose of returning an access token. The getToken function checks if the token has expired and if true, it will try fetching a new accessToken given the refreshToken. However, it is important that only one request goes out attempting to refresh the token. Therefore, if the function gets called in parallel, it needs to wait until the first call has completed and return that result for all other calls.

This is my current implementation:

class TokenService {
    private isRefreshing: boolean = false

    public getToken = async (): Promise<string> => {
        const accessToken = window.localStorage.getItem('accessToken')
        if (!accessToken) return Promise.resolve(null)

        // Check if token has expired
        const { exp } = JSON.parse(atob(accessToken.split('.')[1]))
        if (new Date() < new Date(exp * 1e3))
            return Promise.resolve(accessToken)
            

        if (this.isRefreshing) {
            // Wait for request to finish and return result
        }

        // Refresh token
        this.isRefreshing = true
        const refreshedAccessToken = await this.refreshToken()
        return Promise.resolve(refreshedAccessToken)
    }
}

I have added a flag isRefreshing indicating if there is currently a refresh request in progress. I am unsure how to go from here. How do I wait for the request to finish and return its result?

CodePudding user response:

You must reference isRefreshing by going through this first - it's not a standalone identifier.

When refreshing the token, assign the result of the Promise to a property on the class. This will let further calls of getToken to return that Promise if it exists. If it doesn't exist, and if the current token has expired, generate another.

Because you sometimes do Promise.resolve(null), you sometimes return null, not a string, so you should change the type signature of getToken appropriately.

class TokenService {
    private tokenPromise: null | Promise<string> = null;

    public getToken = async (): Promise<string | null> => {
        const accessToken = window.localStorage.getItem('accessToken')
        if (!accessToken) return null;
        // Check if token has expired
        const { exp } = JSON.parse(atob(accessToken.split('.')[1]))
        if (new Date() <= new Date(exp * 1e3)) {
            // no need to retrieve another
            return accessToken;
        }
        if (this.tokenPromise) return this.tokenPromise;
        this.tokenPromise = this.refreshToken()
            .catch((error) => {
                window.localStorage.removeItem('accessToken');
                return null;
            })
            .finally(() => {
                this.tokenPromise = null;
            });
        return this.tokenPromise;
    }
}
  • Related