3

I know this is a terrible idea. But I have an API which I can't use until I have a piece of data which I can only get asynchronously. Something like this:

const key = await get_async_data(config) // NOT RIGHT, can't use await at top level
const api = new API(key)
... use api ...

This is at top level, outside of any function, so I can't just await get_async_data() (it does return a Promise). Is there anything short of putting all my code in a giant async function so I can call await?

API is just a class exported by a module (which I control).

(BTW I thought of putting the code to get the key into the API class's constructor, but of course constructors can't be async either.)

I could make every async method of API set the key if unset, but that's pretty invasive and error-prone.

So I'm not really asking how to make my top-level code wait as much as I'm looking for alternative ways to structure this so the async call happens cleanly.

Here's some more detail in case this helps. In api.js:

class API {
  constructor(key) {
    this.key = key
    // other stuff
  }
  async f1(a) {
  }
  async f2(b, c) {
  }
  f3() {
    return true
  }
}
export default API

Then in the places (many) where it'll be used:

import API from '@/api'

const key = async get_key() // NOPE
const theAPI = new API(key)

async importantMethod(args)
{
  return await theAPI.f1(args)
}
async otherMethod()
{
  if (theAPI.f3)
    return await theAPI.f2(123)
  // etc...
}
// ... and so on
6
  • 2
    Can you explain the issue you have with wrapping the code in an async function (or just using .then)? It would seem to be the obvious solution Commented Feb 23, 2019 at 0:02
  • I want to use this API everywhere, in all my classes. I can't wrap everything in a giant async function. Commented Feb 23, 2019 at 0:04
  • 1
    Not sure what you mean, but you'd need to await for this function everywhere you need to wait for the promise to resolve before continuing. Or if you dont want to wrap everything in an async function just do use .then instead. Commented Feb 23, 2019 at 0:08
  • Can you give a more concrete example with more code, so we can see what looks too messy to you? It's not all that clear ATM Commented Feb 23, 2019 at 0:09
  • Just call the api, stuff the resulting promise in a var, and await as needed. Commented Feb 23, 2019 at 0:16

3 Answers 3

1

Just use the Promise:

const pendingAPI = get_async_data(config).then(key => new API(key)); // N.B. no await
export default pendingAPI;

Meanwhile, in another file...

import pendingAPI from 'other/file';
pendingAPI.then(doStuffWithAPI);

There are times when async/await is a win. But never forget it's just sugar over Promises.

Sign up to request clarification or add additional context in comments.

Comments

0

top level is a terrible idea, yes. But I don't see why you can't just put it in a function?

const getAsync = async () => {
  const key = await get_async_data(config);
  return key;
}
getAsync().then(key => {
  const api = new API(key)
}

6 Comments

No need for await / async if you're using .then, just do get_async_data(config).then(key =>
Also, it's not so much a "terrible idea" (it's not a bad idea, there's currently a proposal for top-level await) as an idea that's not currently permitted by the spec.
sure, but he seems to want a smaller function available to use elsewhere. Maybe to involve other parameters? and whether it's just pure promise or written as async await is neither here nor there.
re proposal, I didn't know that, interesting. I would still say that doing any functionality in global is generally to be avoided but I understand there are other POVs on that. I guess top level await would make it feel a bit more PHP / Python-y.
Top level in a project doesn't necessarily mean global, luckily - that's what module bundlers are for.
|
0

If you want to alter your existing code as little as possible, I'd consider changing the entry point to a module which gets the key, and then calls the module which instantiates the API (the old entry point). For example:

// start.js
import makeApi from './makeApi';
get_key()
  .then(makeApi);

// makeApi.js
export default function(key) {
  const theApi = new API(key);
  function importantMethod(args) {
    return theAPI.f1(args)
  }
  function otherMethod() {
    if (theAPI.f3)
      return theAPI.f2(123)
  }
  // etc
}

In short, all you have to do is wrap your current entry point in a function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.