Home > Enterprise >  Given Two async functions, which function runs first?
Given Two async functions, which function runs first?

Time:05-23

const [counter, setCounter] = useState(0)

Let's say I have two async functions func1 and func2. Both functions are asynchronous and update the counter state. func1 runs before func2.

async function func1() {
    ....extra code
    setCounter(counter   1)
}

async function func2() {
    ....extra code
    setCounter(counter   1)
}

func1()
func2()

Questions:

  1. Is it guaranteed that when func2's useState runs, the Counter's state is updated by func1? Please keep in mind both functions are asynchronous.
  2. If not, what should I do to make sure that func2's use State runs only after func1's Usestate has updated the Counter's value.

Here both func1 and func2's use state do the same job of incrementing Counter by 1. No matter which function runs first, the output is going to be the same. But while answering please answer as if both states do the different tasks and in order to achieve the desired result, func1 should run before func2.

CodePudding user response:

Is it guaranteed that when func2's useState runs, the Counter's state is updated by func1?

No, not at all.

If not, what should I do to make sure that func2's use State runs only after func1's Usestate has updated the Counter's value.

You could make func2 wait until the promise returned by func1 settles (async functions always return a promise), but that wouldn't necessarily mean that func2 didn't start running before the setCounter state update had occurred; state updates are asynchronous, too.

To be sure of that, you need to both wait for func1's promise to settle and use the callback form of setCounter: setCounter(c => c 1):

async function func1() {
    // ....extra code
    setCounter(counter   1);
}

async function func2() {
    // ....extra code
    setCounter(c => c   1); // Note the callback form of state setter
}

func1().then(() => func2);

There are a dozen ways to spin that. For instance, you could pass func1's promise into func2 as an argument and have it only await it at the very end:

The basic idea is to make func2 wait until func1 is done.

async function func1() {
    // ....extra code
    setCounter(counter   1);
}

async function func2(promise) {
    // ....extra code
    await promise;          // Wait for `func1` to finish
    setCounter(c => c   1); // Note the callback form of state setter
}

func2(func1());

In the vast majority of cases, you probably don't care whether func1 is done or not; just use the callback form of setCounter in both places so that neither of them is using outdated state (the counter they close over):

async function func1() {
    // ....extra code
    setCounter(c => c   1);
}

async function func2() {
    // ....extra code
    setCounter(c => c   1);
}

func1();
func2();

In fact, I'd argue that any time you're using existing state (counter's previous value) to update state (incrementing it by one), you should use the callback form of the setter. It's really easy to close over a stale copy of the state variable; using the callback form ensures you're using up-to-date state.

CodePudding user response:

No, it's definitely not guaranteed that when func2 runs func1 has already updated the state. What you can do to achieve that with a functional component is using the useEffect hook to track the changes on the state counter:

useEffect(() => {
 func2();
}, [counter])

By doing this you are running func2 every time the counter state has changed. Also, if you want to make sure that func2 gets run only once after func1 updated the state, you can use a flag like this:

const isStateUpdatedByFunc1 = useRef(false);

async function func1() {
    ....extra code
    isStateUpdatedByFunc1.current = true;
    setCounter(counter   1);
}

useEffect(() => {
 if(isStateUpdatedByFunc1.current){
  isStateUpdatedByFunc1.current = false;
  func2();
 }
}, [counter])
  • Related