0

What is the most up to date method to ensure that some asynchronous code completes in a class constructor before that class is subsequently used?

Specifically, how would an API client class retrieve an access token before allowing more method calls, as shown below?

class API_Client {

    constructor(...) {

        # Below should 'block' other method calls until token is assigned
        this.login().then(res => {
            this.token = res.data.token;
        });

    }

    async login() {
        return makeRequest(...) # <-- Promise which returns access token data
    }
}

const client = new API_Client(...);
client.someAuthOnlyMethod() # <-- Should only happen after the `login` method completes.

I found older answers, yet couldn't quite understand how to solve the problem posed in the first comment left on the linked answer.

JD Angerhofer
  • 148
  • 1
  • 13
  • 2
    The most up-to-date method is still [not to put any asynchronous stuff in the constructor](https://stackoverflow.com/a/24686979/1048572). – Bergi Jan 17 '18 at 16:41
  • If you need the result of an asynchronous operation to construct an object then the most up to date method is to invoke your constructor function in the `.then()` stage of a promise. – Redu Apr 19 '18 at 22:14

3 Answers3

2

The most up-to-date method is still not to put any asynchronous stuff in the constructor. In your specific case, that's

class API_Client {
    constructor(token) {
        this.token = token;
    }
    static async createLoggedIn(…) {
        const res = await makeRequest(...) # <-- Promise which returns access token data
        return new this(res.data.token);
    }
}

const client = await API_Client.createLoggedIn(…);
client.someAuthOnlyMethod()
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Me thinks you need to at least show some error handling that handles the case when login fails because that's the part that is a little more complicated when you create a factory that does multiple things. – jfriend00 Jan 17 '18 at 17:12
  • @jfriend00 it just fails with a promise rejection. You'd put the `await` inside a `try`/`catch` whereever you want to handle an error (probably when calling `API_Client.createLoggedIn`). A factory that does multiple things would just throw different exceptions, wouldn't it? – Bergi Jan 17 '18 at 17:21
  • Yeah, I know that. Not sure the OP knows that. – jfriend00 Jan 17 '18 at 17:21
1

You could store the token as a promise:

class API_Client {

    constructor(...) {

        # Below should 'block' other method calls until token is assigned
        this.token = this.login()
          .then(res => res.data.token)

    }

    async someAuthOnlyMethod() {
      let token = await this.token;
      //...continue
    }

    async login() {
        return makeRequest(...) # <-- Promise which returns access token data
    }
}

const client = new API_Client(...);
client.someAuthOnlyMethod() # <-- Should only happen after the `login` method completes.
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    Don't forget to install a default error handler in the constructor to avoid unhandled promise rejections – Bergi Jan 17 '18 at 16:58
  • Interesting. I don’t think I have ever thought to save a promise into a variable, but it’s intuitive enough. The primary drawback I see here is that the token would need to be set up repeatedly on each authenticated ‘call’ — hardly the end of the world, but less than optimal. – JD Angerhofer Jan 17 '18 at 18:05
-1

You shouldn't be calling any asynchronous code from a constructor to begin with. In the case above, your makeRequest function would worry about the login token.

There is also no real value in a class in this case. You should just export a series of functions to make the API calls.

Keith Rousseau
  • 4,435
  • 1
  • 22
  • 28
  • Hmm, my thought was that this class would support dozens of different endpoint requests as well as repeated requests by the same 'user'. Why is a class with many methods not an appropriate solution to store an access token for repeated use across many different requests? – JD Angerhofer Jan 17 '18 at 16:41
  • I guess my question to you is what is the benefit of a class vs a module where you export individual functions for each endpoint? Javascript is not a class-based language - it's more of a slapped on feature and you're not really getting any benefit from a class here. – Keith Rousseau Jan 18 '18 at 12:26