Click here to Skip to main content
15,885,537 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
Have a problem that I feel like must have been solved in some elegant ways but wanting some input. I'm using vanilla Javascript to solve this but wondering if perhaps there is a more elegant React solution or some other library.

Problem: Have an API that requires a bearer token that only lasts for 15 minutes. Every time a request for data is made we need to check if the token is valid, and if not, request a new token. The problem is that there is time between when a new token is requested and received. Meanwhile more requests for data could have been made resulting in another unnecessary request for a new token.

JavaScript
const api = new testApi();
api.getSomeData(1);
api.getSomeData(2);
api.getSomeData(3);


This is obviously a super simplified test to demonstrate.

The first call to getSomeData happens. Because there was no initial token it requests a new one then returns its data. However, because of the asynchronous nature of Javascript, the second 2 calls to getSomeData fire before the first getSomeData has received the new token. Therefore the 2 subsequent calls to getSomeData also request a new token.

What I have tried:

Here is the full class I created that I believe solves the issue (but still wondering if this is the best solution):

JavaScript
class testApi {
  constructor() {
    this.token = "";
  }

  // Return a promise with the resulting token
  getToken() {
    this.token = new Promise((resolve, respond) => {
      setTimeout(() => {
        console.log("Got a new token!");
        resolve(Math.random() * 1000);
      }, 1000);
    });
  }

  // Simple check to determine if token is valid
  isValidToken() {
    if (this.token === "") return false;
    return true;
  }

  // Call to api to get some data
  getSomeData(callNum) {
    if (!this.isValidToken()) {
      console.log("Token not valid so we need to get another");
      this.getToken();
    }

    // Token is always a promise so we use .then to make sure it's current
    this.token.then((result) =>
      console.log(`Token for call ${callNum} is ${result}`)
    );
  }
}

const api = new testApi();
api.getSomeData(1);
api.getSomeData(2);
api.getSomeData(3);


By setting this.token to a promise that is returned by the request for the new token (simulated here by a setTimout), any request for that token must use .then to get a valid token.

In getSomeData, the valid token only appears in the callback of this.token.then(). If we would try to access this token outside of the .then it would return an unfulfilled promise.

The result of this code is:

JavaScript
Token not valid so we need to get another
Got a new token!
Token for call 1 is 897.2653861706979
Token for call 2 is 897.2653861706979
Token for call 3 is 897.2653861706979


Thoughts on a better methodology for this?

thanks!
Posted
Updated 16-Sep-20 5:54am

1 solution

I'm not particularly au fait when it comes to modern JS and the classes available to it, but if you're concerned about asynchronous web requests refreshing the token at the same time, you'd have to look into something like a Mutex. For the flow of operations I'd expect something like:

  • Check if token is valid (false)
  • Acquire the mutex
  • Check if token is still invalid (another async operation could have changed this)
  • Refresh the token
  • Release the mutex
  • Proceed

The key part here is the first check for an invalid token can occur at the same time as other async operations, and then the next time we call it we've done it from the mutex so we're guaranteed that only one async operation can do this at a time. This saves on acquiring a mutex every time, which would impact performance.

In terms of your code, I expect you'd replace the token with a new Promise, which can then be leveraged via the then() method.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900