Some important points that I would like to cover here before answering your question are as follows:
We can easily do asynchronous calls within useEffect() hook.
The call-back function within useEffect() cannot be async, so callback cannot use async-await, because useEffect() cannot return a promise which every async function does by default:
useEffect(async ()=>{
})
// This will return error and will not work
So we can set async function inside the callback function or outside in our component.
Now coming to your question:
useEffect(() => {
setTimeout(() => {
setSomeState(complexComputation(someDependency));
}, 0);
}, [someDependency]);
The main point here is that our useEffect() hook will run initially, and setTimeout() function is simply passed to the Web-API of Browser, Timer starts normally and only once all the code is executed on the call-stack, the callback within setTimeout gets executed.
Check running the below code.
useEffect(() => {
console.log("Line one")
setTimeout(() => {
setSomeState(complexComputation(someDependency));
console.log("line two");
}, 3000);
console.log("Line three");
}, [someDependency]);
Output will be:
Line one
Line Three
Line two // after all other synchronous consoles are executed :)
The question is not about, "what running of useEffect() asynchronously or synchronously mean", but about in what scenarios does useEffect() run first, which runs in both the scenarios.
And since in your code you as using the second argument and passing the value (a state). i-e.,
useEffect(()=>{},[someDependency])
Whenever someDependency again the component re-renders and our useEffect() is again invoked and the code (asynchronous or synchronous) gets executed.