How JavaScript Decides What Runs Next
This lesson shows how JavaScript decides what runs when. JavaScript runs your code on one thread. That means it does one thing at a time. Two of your functions never run at the same moment.
So how can it wait for timers and promises? With the event loop. It runs all the normal code first. Then it takes the next waiting callback, runs it to the end, takes the next one, and so on.
Some callbacks get to go first. This example shows the order. Promise.resolve() makes a promise that is already resolved, so its .then callback is ready at once:
console.log("first");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("last");
Output:
first
last
promise
timeout
Here is why:
- The normal code runs first, all of it. That prints
firstandlast. The two callbacks are only scheduled. - Next come promise callbacks. They wait in the microtask queue. That prints
promise. - Then come timer callbacks. They wait in the task queue. That prints
timeout, even with a delay of 0.
A queue is a waiting line: the first callback in is the first one out. When both are ready, promise callbacks run before timer callbacks, even when the timer was scheduled first.
This has two results you will meet in real programs.
A timer delay is a minimum, not an exact time. A 100 ms timer runs at the first free moment after 100 ms. If normal code is still running, the timer waits for it.
Long-running code blocks everything. While a slow loop runs, no callback can run. In a browser, the whole page freezes. Here Date.now() gives the current time in milliseconds, and the loop keeps the thread busy for 300 ms:
const start = Date.now();
setTimeout(() => {
const waited = Date.now() - start;
console.log("timer ran late:", waited >= 300);
}, 0);
while (Date.now() - start < 300) {
// busy, nothing else can run
}
console.log("loop done");
Output:
loop done
timer ran late: true
The timer asked for 0 ms but had to wait until the loop was done. This is why JavaScript code avoids waiting and blocking. Slow work is scheduled with a callback or a promise, so the thread stays free.
await fits the same picture. When a function reaches await, it pauses, and the thread is free for other callbacks. The rest of the function runs later, when the promise is resolved.
In the exercise, add one line that prints payment checked before the timer's message, even though the timer is written first.