The event loop
Some actions take time: waiting for a timer, a server response, a file download. JavaScript does not stand still while they happen; it carries on and comes back to the result later. To write such code without surprises, you need to know in which order it runs.
Synchronous code
Ordinary code is called synchronous: statements run strictly in order, and the next one starts only after the previous one has finished:
console.log('One');
console.log('Two');
console.log('Three');
// One
// Two
// ThreeJavaScript is single-threaded: at any moment it runs only one statement. Two functions cannot run at the same time.
Asynchronous code
Asynchronous code is code that starts now and finishes later. The setTimeout() function postpones a call to another function by a given number of milliseconds:
setTimeout(() => {
console.log('300 ms have passed');
}, 300);
// 300 ms have passedWhile the timer waits, JavaScript is free to run other code.
The task queue
When a timer fires, its function does not run immediately; it joins the task queue. JavaScript takes tasks from the queue only once the current code has finished:
console.log('Start');
setTimeout(() => {
console.log('Timer');
}, 0);
console.log('End');
// Start
// End
// TimerEven with a zero delay the timer ran last: all the main code finished first.
The event loop
The mechanism that checks the queue over and over and starts the next task is called the event loop. Its work can be described like this:
| Step | What happens |
|---|---|
| 1 | All the current synchronous code runs |
| 2 | All microtasks run — promise handlers |
| 3 | One task is taken from the queue, for example a timer function |
| 4 | Back to step 2 — as long as there are tasks |
Microtasks
Promise handlers (covered in the next lessons) go into a separate microtask queue. It runs before ordinary tasks:
setTimeout(() => console.log('Timer'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('Plain code');
// Plain code
// Promise
// TimerLong code delays everything
While synchronous code is running, no task from the queue can start. This loop keeps JavaScript busy for 200 ms, and the timer waits for it to end:
const start = Date.now();
setTimeout(() => {
const waited = Date.now() - start;
console.log(waited >= 200); // true
}, 0);
while (Date.now() - start < 200) {
// just waiting
}In the browser the whole page freezes for that time: buttons do not respond and animations stop.
Common mistake
Expecting setTimeout(..., 0) to run immediately. The delay is only the minimum waiting time: the function starts no earlier than the current code and all microtasks have finished. If you need the result right away, do not put the code in a timer; if you need "first this, then that", use promises and await.