0

I have an API call that I want to be called in one function, but awaited in a different callback.

E.g. I want to make a GET request on load so that the data is available as soon as possible, but I need a "submit" function to await the GET request so that it doesn't fire before the data is available.

This is due to a race condition I have between 2 asynchronous functions, where depending on API response time, could take more or less time than an authentication callback that has the control logic.

I'd rather perform both calls at the same time so that it can run as fast as possible in any scenario. I know this has higher cost since the data may not be used, but it's only done once per session on initial connection, so the potential decrease in load time is worth the extra potential extra API call.

That said, here's what I'm currently doing:

  let initialized = false;
  let dataError = null;
  let dataWaiters = [];
  const dataLoaded = () => new Promise(( resolve, reject ) => {
    if ( initialized ) resolve();
    if ( dataError ) reject( dataError );
    dataWaiters.push({ resolve, reject });
  });

This allows me to make my axios get request on load, which will then resolve any / all promises in dataWaiters.

async onStart () {
  // Attempt to use a local copy if we can
  if ( session.has.token.already ) {
    loadFromCache();
    return;
  }

  // Else we need to get data from API server
  try {
    const { data } = await axios.get('https://my-api-server/api/temp-token');
    saveResposne( data );
    this.initialized = true;
    dataWaiters.forEach( waiter => waiter.resolve() );
  } catch ( err ) {
    dataError = err;
    dataWaiters.forEach( waiter => waiter.reject(err) );
  }
}

Then in another callback function I can simply use await dataLoaded() to delay execution until this request had completed.

async onSomeCallback( data ) {
  if ( useTempToken ) {
    await dataLoaded();
    // use token...
  else {
    // use data...
  }
}

This works, but I feel like there should be a way I can use the axios.get promise in a better way, and maybe use fewer 'global' variables. Is there a way I can handle this more elegantly?

13
  • I'm a little confused ... where is the second asynchronous call? It looks like you only have one in the second code block. Commented Dec 6, 2019 at 21:34
  • Also, where does saveResponse come from? And useTempToken? Commented Dec 6, 2019 at 21:36
  • @djfdev onStart and onSomeCallback run in the same context. onStart at load / mount, and onSomeCallback runs after firebase has authenticated a user. If the user is not authenticated, I need to send a temporary auth to another server in order to access data. I don't want to wait for firebase to return before getting the temp auth though, and I can't run the onSomeCallback function without having either a temp auth or user auth. Commented Dec 6, 2019 at 21:41
  • What is saveResponse? And where is the Firebase logic? The example code seems incomplete. It would be helpful to see where the onSomeCallback function is actually called. Commented Dec 6, 2019 at 21:47
  • It is incomplete, I stripped out the logic that wasn't relevant because I'm only interested in a way to await a single promise (in this case a get request) in multiple places. The way the data is passed around and the related firebase functions aren't related and are just an example of the use case I have. Commented Dec 6, 2019 at 21:53

1 Answer 1

1

Here is example of this working, very dumbed down. We declare a new Event. Then our data loader dispatches when ready. We have an event listeners that trigger two actions.

const data = [];

const dataReady = new CustomEvent('DataReady', {
  bubbles: true,
  detail: data
});

const populateData = () => {   
  setTimeout(() => {
    for(i = 0; i< 10; i++) data.push(i);
    console.log('test2');
    document.dispatchEvent(dataReady);
  }, 3000);
};

document.addEventListener('DataReady', ({ detail }) => {
  console.log(detail);
});

document.addEventListener('DataReady', ({ detail }) => {
  console.log(`Array length: ${detail.length}`);
});

populateData();
console.log('Test');

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

2 Comments

Looks logical enough to me. Thanks
No problem happy coding :)

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.