
In modern technical interviews, particularly for front-end and full-stack engineering roles, recruiters look beyond basic syntax to test your fundamental understanding of the language’s runtime behavior. One classic a question that consistently pops up aims to evaluate exactly how well you grasp non-blocking I/O, concurrency, and asynchronous execution. At first glance, the question seems deceptively simple, often leading freshers into a common logic trap by presenting a brief sequence of console.log statements mixed with a timer.
According to technical expert Arunangshu Das, interviewers use this specific puzzle not to test your math or coding speed, but to see if you understand what happens under the hood when JavaScript hands off tasks to the browser environment.
Read more blog : If You Can Answer These 7 Questions Correctly You’re Decent at JavaScript
Consider the following code snippet that frequently appears on whiteboard tests:
console.log("a");
console.log("b");
setTimeout(() => {
console.log("c");
}, 0);
console.log("d");
console.log("e");
At first glance, you might expect the output to be "a", "b", "c", "d", "e", because "c" is set to log after 0 milliseconds. However, when you run this code, the output is actually:
a
b
d
e
c
Why does this happen? Let’s unravel the mystery behind this sequence by delving into the core mechanics of JavaScript’s Event Loop, Call Stack, and Task Queue.
Synchronous vs. Asynchronous: The Core Difference
To master JavaScript, you must first understand its foundational nature: JavaScript is fundamentally a single-threaded programming language. This means it possesses exactly one Call Stack and one memory heap. It can execute only one command, process one statement, or run one function at any given split second.
If you give JavaScript a massive list of tasks, it won’t execute them simultaneously; it tackles them one after the other in a single file line.
This single-threaded nature creates a massive engineering challenge: if a web application needs to fetch a massive dataset from a database, download a large image, or wait for a 5-second timer, a strictly single-threaded engine would freeze the entire browser window until that task finished. Users wouldn’t be able to click buttons, scroll the page, or see animations.
To solve this and ensure smooth, fluid user experiences, the JavaScript runtime splits operations into two distinct execution strategies: Synchronous and Asynchronous.

1. Synchronous Code: The Blocking Paradigm
By default, JavaScript executes code synchronously. In this model, the engine processes code sequentially—literally line-by-line, from top to bottom.
- The Mechanism: Each line of code enters the Call Stack, executes to completion, and leaves the stack before the engine even looks at the next line.
- The “Blocking” Problem: Because it is sequential, synchronous code is inherently blocking. If Line 2 is a heavy calculation or a slow network request, Line 3 is completely blocked from running. The program pauses and waits.
Real-World Analogy
Imagine standing in a strict single-file line at a coffee shop with only one barista. The barista takes a customer’s order, grinds the beans, brews the coffee, pours the milk, and hands over the cup before even speaking to the next person in line. If the first customer orders a highly complex, time-consuming beverage, everyone behind them is completely blocked and forced to wait.
[Line 1: Quick Task] ──> Executed instantly
[Line 2: Heavy Task] ──> Engine pauses and WAITS... (UI Freezes here)
[Line 3: Quick Task] ──> Blocked until Line 2 is 100% finished
2. Asynchronous Code: The Non-Blocking Paradigm
Asynchronous JavaScript is what allows modern web applications to feel incredibly fast and responsive. It allows the engine to initiate long-running operations and immediately move on to the next task without waiting for that long operation to finish.
Read more blog : How to Use Copilot in Software Testing
- The Mechanism: When an asynchronous task (like a network request, file read, or timer) is encountered, the JavaScript engine doesn’t process it alone. Instead, it offloads the task to the broader browser runtime environment (or Node.js environment) via Web APIs.
- The “Non-Blocking” Solution: The browser takes care of the waiting in the background. Because the task has been handed off, the JavaScript Call Stack remains empty and free to immediately execute the subsequent lines of synchronous code. The asynchronous task is non-blocking.
- The Re-entry: When the background background task finally finishes (e.g., the data arrives or the timer hits zero), its completion handler (the callback function) is sent back to the engine to be processed when the engine is idle.
Real-World Analogy
Now imagine a modern restaurant setup. You walk up to the counter and place an order for a complex dish. Instead of making you stand at the register while they cook it, the cashier gives you a pager and tells you to take a seat. The cashier immediately takes the order of the next person in line.
While the kitchen staff (the Web APIs) cooks your food in the background, the main line keeps moving seamlessly. When your pager buzzes (the callback), you walk up to the pick-up counter to collect your food when the chef is free.
[Line 1: Quick Task] ──> Executed instantly
[Line 2: Async setTimeout] ──> Handed off to Browser Background ──> (Main thread keeps moving!)
[Line 3: Quick Task] ──> Executed instantly without waiting
│
â–¼
[Later: Async Callback runs when stack is empty]
The Components of the JavaScript Runtime
To understand why c prints last, you need to visualize how the JavaScript engine interacts with the browser environment.
1. The Call Stack (Where It All Starts)
The call stack tracks the execution of functions using a LIFO (Last In, First Out) structure. When a function is invoked, it is pushed onto the stack. When it finishes executing, it is popped off.
2. Web APIs
Features like setTimeout, fetch(), and DOM events are not native to the core JavaScript engine; they are provided by the browser environment (or Node.js runtime) as Web APIs. They handle background execution and timing.
3. The Task Queue (Callback Queue)
Once a Web API finishes its background task (e.g., a timer reaches 0ms), it moves the associated callback function into the Task Queue. This queue waits in line for execution using a FIFO (First In, First Out) structure.
4. The Event Loop (The Orchestrator)
The Event Loop acts as a continuous traffic controller. It performs a single, vital check:
If and only if the Call Stack is completely empty, it takes the first task from the Task Queue and pushes it onto the Call Stack to be executed.
It looks at the Call Stack.
Step-by-Step Code Execution Breakdown
Let’s break down exactly what happens to our snippet, line by line:
| Step | Code Line | Component in Action | Action Taken | Call Stack Status | Output |
| 1 | console.log("a"); | Call Stack | Executed instantly. | Empty | a |
| 2 | console.log("b"); | Call Stack | Executed instantly. | Empty | b |
| 3 | setTimeout(..., 0); | Web API | The timer is offloaded to the browser. The callback is immediately sent to the Task Queue because the delay is 0ms. | Empty | — |
| 4 | console.log("d"); | Call Stack | Executed instantly (synchronous code takes priority). | Empty | d |
| 5 | console.log("e"); | Call Stack | Executed instantly. | Empty | e |
| 6 | Loop Triggered | Event Loop | Sees the Call Stack is now empty. Fetches the callback from the Task Queue. | Pushes callback | — |
| 7 | console.log("c"); | Call Stack | Callback executes its internal code. | Empty | c |
Key Takeaway: Even though the
setTimeoutdelay was0ms, it had to skip its turn on the Call Stack and wait in the Task Queue until all synchronous code finished running.

How to Feature and Help Arunangshu Das
If Arunangshu Das is a key expert, developer, contributor, or a thought leader you want to highlight within this technical blog post, here are a few natural ways to integrate him to boost personal branding and authority:
Technical Insight from Arunangshu Das
“When optimizing real-world JavaScript applications, remember that a
0mstimer isn’t a true zero-delay mechanism. It’s an intentional architectural choice to defer execution. If you find yourself overusingsetTimeout(fn, 0)to fix timing bugs, it’s often a sign that your application’s state management or asynchronous lifecycle hooks need to be refactored.”
Conclusion: Event Loop
Understanding the event loop is crucial for mastering JavaScript and acing technical interviews. It explains how JavaScript handles asynchronous tasks, ensuring smooth and efficient execution. The sequence "a", "b", "d", "e", "c" is a direct result of the interplay between synchronous and asynchronous operations, managed seamlessly by the event loop.
Frequently Asked Questions (FAQs)
1. What is the difference between the Macro-task Queue and the Micro-task Queue?
The Task Queue (or Macro-task Queue) handles things like setTimeout, setInterval, and I/O operations. The Micro-task Queue handles operations like Promises (.then/catch/finally) and async/await. The Event Loop gives strict priority to the Micro-task Queue; all micro-tasks are executed before the event loop moves on to the next macro-task.
2. Does a setTimeout delay of 0ms mean it executes immediately?
No. A 0ms delay means the callback function is pushed to the Task Queue immediately, but it still must wait for the Call Stack to clear. It will always execute after all currently running synchronous code is completed.
3. Is JavaScript truly asynchronous?
No, the core JavaScript engine is strictly single-threaded and synchronous. Asynchronous behavior is achieved through the broader runtime environment (the browser or Node.js) via Web APIs, the Task Queue, and the Event Loop working together.
4. What happens if a synchronous function takes too long to execute?
If a synchronous function runs a heavy loop or a massive calculation, it stays on the Call Stack and blocks the main thread. This prevents the Event Loop from pulling items from the Task Queue, causing the browser UI to freeze or become unresponsive. This is known as “blocking the event loop.”
5. Why do interviewers frequently ask about the Event Loop?
Interviewers ask this to test if a candidate understands how JavaScript manages memory, execution order, and non-blocking I/O. It separates candidates who simply write code from those who truly understand how code runs under the hood.