Hello! Welcome to your next lesson.
In our last session, we dynamically rendered a list of projects onto a page from a JavaScript array. That data was localβit was written directly into our .js file. In the real world, data usually lives on a server, and we need to fetch it over the network.
Network requests take time. If JavaScript, which runs on a single thread, simply paused and waited for the data, your entire webpage would freeze. You wouldn't be able to click buttons, scroll, or interact with anything. This is a terrible user experience.
To solve this, we must understand asynchronous programming. This lesson's outcome is to explain the difference between synchronous and asynchronous JavaScript using a setTimeout example. This concept is the absolute bedrock for working with APIs, handling user events, and building modern, responsive web applications.
1. The Synchronous World: One Thing at a Time
By default, JavaScript is a synchronous, single-threaded language. This means it executes code one line at a time, in order. Each operation must complete before the next one can begin. This is often called "blocking" behavior.
To manage the order of execution, JavaScript uses a mechanism called the Call Stack. Think of it like a stack of plates:
- When a function is called, it's placed on top of the stack.
- If that function calls another function, the new function is placed on top.
- When a function finishes, it's taken off the top of the stack.
- The program continues with whatever is left on top, until the stack is empty.
The following video provides an excellent visualization of the call stack and demonstrates the problem of blocking.
What the heck is the event loop anyway? | Philip Roberts | JSConf EU
This segment from the talk 'What the heck is the event loop anyway?' by Philip Roberts clearly illustrates how synchronous code runs on the call stack and why slow operations can 'block' the browser, making it unresponsive.
Please watch these two clips: The Call Stack (04:08 - 07:18): This part visualizes how functions are added to and removed from the call stack in a synchronous sequence. The Problem of Blocking (07:18 - 10:24): Here you'll see a practical demonstration of what happens when a slow (blocking) task runs on the stackβthe entire user interface freezes.
This freezing, or "blocking," is exactly what we need to avoid when making a network request.
2. The Asynchronous Solution: The Event Loop
So, how does JavaScript handle time-consuming tasks without freezing? It uses asynchronous operations. An asynchronous operation allows the engine to start a task (like a timer or a network request), move on to the next lines of code immediately, and then handle the result of that task when it's ready.
We can demonstrate this with a simple browser function: setTimeout. It takes a function (a "callback") and a delay in milliseconds.
Consider this code:
console.log("First");
setTimeout(() => {
console.log("Second (from the timer)");
}, 2000); // 2000ms = 2 seconds
console.log("Third");
If you run this, the console output is:
First
Third
Second (from the timer)
The JavaScript engine did not wait for two seconds. It started the timer, continued on to log "Third", and only after the timer finished did "Second" appear. How is this possible?
The answer lies in the fact that the browser environment is more than just the JavaScript engine. It also includes:
- Web APIs: These are features provided by the browser (not the JS engine itself), like the DOM, AJAX (
fetch), and timers (setTimeout). They can do work in the background. - Callback Queue (Task Queue): A waiting area for callback functions that are ready to be executed.
- The Event Loop: A simple process with one job: continuously check if the Call Stack is empty. If it is, it takes the first function from the Callback Queue and pushes it onto the Call Stack to be run.
This diagram illustrates the whole system:

The next segment of the same video is the most famous explanation of this process. It will walk you through exactly what happens when you call setTimeout.
What the heck is the event loop anyway? | Philip Roberts | JSConf EU
This is the core of our lesson. Philip Roberts explains how the Call Stack, Web APIs, Callback Queue, and Event Loop all work together to achieve non-blocking, asynchronous behavior.
Watch the section from 10:24 to 14:55. Pay very close attention to how setTimeout is handled by the Web API, how its callback function is moved to the queue, and when the event loop finally pushes it to the stack.
3. A Classic Example: setTimeout(0)
Now for a classic question that truly tests this understanding. What happens when you use setTimeout with a delay of 0?
console.log("A");
setTimeout(() => {
console.log("B (with a 0ms timer)");
}, 0);
console.log("C");
You might expect the output A, B, C. However, the actual output is A, C, B.
Why? Because even with a zero-millisecond delay, setTimeout is a Web API.
console.log("A")runs (pushed to stack, runs, popped off).setTimeoutis called. The JS engine passes the callback function (() => console.log("B")) to the Web API and immediately moves on. The stack is now free of thesetTimeoutcall.- The Web API's timer finishes instantly (0ms) and places the callback into the Callback Queue.
- Crucially, the Event Loop cannot act yet because the main script is still running. The Call Stack is not empty.
console.log("C")runs (pushed to stack, runs, popped off).- Now, the main script has finished, and the Call Stack is finally empty.
- The Event Loop checks the queue, finds the waiting callback, and pushes it to the stack.
- The callback runs, and
console.log("B")is executed.
This demonstrates that setTimeout(fn, 0) doesn't run the function immediately, but rather defers it to run after the current synchronous code on the call stack has finished.
4. Your Turn: See it in Action
Let's solidify this with a quick, hands-on test.
- Create a new folder for this mini-exercise. Inside, create
index.htmlandapp.js. - In
index.html, add the basic HTML boilerplate and a button. Link yourapp.jsfile at the bottom of the<body>.<!DOCTYPE html> <html lang="en"> <head> <title>Async Test</title> </head> <body> <h1>Event Loop Test</h1> <button id="my-button">Run Code</button> <script src="app.js"></script> </body> </html> - In
app.js, add the following code:const myButton = document.querySelector('#my-button'); myButton.addEventListener('click', () => { console.log('Button Clicked! Starting synchronous task...'); // A simple synchronous task for (let i = 0; i < 1000000000; i++) { // This loop will take a moment to complete // and will block the main thread. } console.log('Synchronous task finished.'); }); console.log('Script loaded!'); - Open
index.htmlin your browser with Live Server and open the developer console. Notice "Script loaded!" appears immediately. - Now, click the "Run Code" button. You will see "Button Clicked!..." in the console, but then the browser will become unresponsive for a few seconds. You can't click the button again or select text. This is blocking behavior. "Synchronous task finished" will appear only after the delay.
Now, let's modify app.js to see the asynchronous difference. Change the event listener to this:
myButton.addEventListener('click', () => {
console.log('Button Clicked! Scheduling asynchronous task...');
setTimeout(() => {
// This now runs asynchronously
console.log('Asynchronous task finished.');
}, 100); // a small delay
console.log('Callback has been scheduled. Main thread is free!');
});
- Refresh the page and click the button again. Observe the console. You'll see "Button Clicked!..." and "Callback has been scheduled..." appear instantly. The browser remains fully responsive. After a brief moment, "Asynchronous task finished." will appear. The main thread was not blocked!
Conclusion
You've just grasped one of the most important concepts in modern web development! Understanding this mechanism is key to writing applications that feel fast and responsive to the user.
Key Takeaways:
- Synchronous code is blocking. It runs sequentially on the Call Stack, and long-running tasks freeze the UI.
- Asynchronous code is non-blocking. It offloads tasks to Web APIs. When complete, a callback is placed in the Callback Queue.
- The Event Loop is the bridge. It moves callbacks from the queue to the stack, but only when the stack is empty.
setTimeoutis a Web API that lets us easily see this asynchronous flow in action, even with a0ms delay.
In our next lesson, we will use this knowledge to tackle a real-world asynchronous task: using the Fetch API with async/await to retrieve data from a public REST API. The fetch function works on the exact same principles you learned today.