GaryO
GaryO

Reputation: 6338

How to wait for an async function in javascript at top level?

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

Upvotes: 3

Views: 2851

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 370859

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.

Upvotes: 0

Jared Smith
Jared Smith

Reputation: 21936

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.

Upvotes: 1

Jonny Rathbone
Jonny Rathbone

Reputation: 179

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)
}

Upvotes: 0

Related Questions