Timeouts can be pretty useful. They can also be confusing when we try and run things in timeouts, and they happen in an order we aren't expecting. So let's walk through how timeouts work to try and get a better grasp on figuring out exactly when our stuff will run.
Here's the function the example we will run. As you see, the loop prints out the number AND sets a timeout with a delay of 0 to print out the same number. What do you think will happen? What should the output be?
const range = 10; for (let i = 0; i < range; i++) { outputElement.innerHTML += `${i}, `; setTimeout(() => { outputElement.innerHTML += `T${i}, `; }, 0); }
Hm, the timeouts with a delay of 0 didn't run immediately. Instead, the loop finished running and then the timeouts ran. Why is that?
Well, when we run a timeout, we aren't saying that the function in the timeout is run when the timer runs out. Instead, what we are saying is that when the timer hits 0, that function is added to the event queue. Everything on the callstack is run in order, and once it is empty, what's in the event queue (our timer callbacks) gets moved to the callstack, and then run. Since the loop is currently running, the callstack isn't empty, and the timeouts wait until the loop is complete before they get moved over and run.
Here's a visualisation of what's happening. Think of the blue block as the next thing to be run. All the red blocks are everything else on the callstack that are waiting for their turn to run, and the green blocks are in the event loop.
Since the timeout has a delay of 0, we see all new blocks get added at the same time to the event queue. Since there are things ahead of them on the callstack to run, we see they aren't actually on the callstack. However, once everything on that callstack is executed, they go onto the callstack, and are then run sequentially.