If an async Vuex action is run twice, can the code following the await call be run in random order?

Viewed 658
const actions = {
  search: debounce(
    async ({ commit, dispatch, getters, rootGetters }, { page = 1 }) => {
      commit("setLoading", true);
      commit("setPage", page);

      console.log("Starting...")
      const randId = Math.random() * 100;
      console.log(randId);

      const results = await EventApi.get();

      console.log("Done: ", randId, `|${results.meta.slept}|`)

      // After `await`
      commit(page == 1 ? "setEvents" : "addEvents", results.events);
      // ... and a bunch more commits after that.

    }, 300, { leading: true, trailing: true })
  }
}

If the action above is called twice:

store.dispatch('search', { page: 1 });
store.dispatch('search', { page: 1 });

Let's assume the request made by the first call takes 10s to complete, and the request made by the second one takes only 1 second.

Is the code after await EventApi.get() in the second call executed after the one in the first call, even thought the request should have finished way earlier? I wouldn't have thought so but my experiment showed that it was the case, so something must be wrong with my code.

Here are the Vuex logs + some of my comments:

https://gist.github.com/niuage/9e2dcd509dc6d5246b3a16af56ccb3d3


In case it helps, here's the simplified EventApi module:

const get = async () => {
  const q = await fetch(someUrl);
  const result = await q.json();

  return result;
}

export default {
  get
}

I'm asking because I'm having an issue where, super rarely, I get my result count out of sync with the results actually displayed, so I was guessing that it could be because there was a race condition between multiple requests.

1 Answers

The second, shorter, call in your example would complete first. This is because the await only waits within the context of the async function.

Since async/await is a different way of writing promises, you can think of it as 2 separate function calls that return a promise. The one would not delay the other.

Here's a demo:

const delay = time => new Promise(resolve => setTimeout(() => resolve(), time))

async function wait(id, time) {
  await delay(time);
  console.log(id + ' complete.');
}

console.log('Starting 1...');
wait('#1', 5000);
console.log('Starting 2...');
wait('#2', 1000);
View the bottom portion of the demo for the console results.

Here's the async function from the demo above written with .then instead:

function wait(id, time) {
  return delay(time).then(() => {
    console.log(id + ' complete.');
  });
}

If you wanted to force the second function call in your example to wait for the first, make the calling context of both an async function, for example:

async created() {
  // Assuming the `search` action returns its http promise!
  await this.$store.dispatch('search', { page: 1 });
  await this.$store.dispatch('search', { page: 1 });
  ...
}
Related