Top Asynchronous JavaScript Interview Questions (2025)
Contents
Introduction
Getting ready for a JavaScript interview? Asynchronous JavaScript interview questions are everywhere in technical interviews. Don’t worry – this guide covers everything you need to know about asynchronous JavaScript, from basic concepts to tricky interview scenarios.
What Makes JavaScript Asynchronous?
Here’s something that confuses many developers: JavaScript is single-threaded, but it can handle multiple tasks at once. How does this work?
The secret lies in how JavaScript engines work with browsers. While JavaScript itself runs on one thread, browsers provide extra tools that make async operations possible.
The Call Stack Explained
Think of the call stack as a stack of plates. When we call a function, it goes on top of the stack. When the function finishes, it gets removed from the top.
Basically call stack is where JavaScript keeps track of function calls. Functions are pushed onto the stack when called and popped off when they return. It’s synchronous — one task at a time.
Let’s see this in action:
function add(a, b) {
console.log("add function called");
return a + b;
}
function calculate(a, b) {
console.log("calculate function called");
return add(a, b);
}
function printTotal(a, b) {
let total = calculate(a, b);
console.log(total);
}
printTotal(4, 5);
When this code runs:

printTotal(4, 5)
goes on the call stack- Inside
printTotal
,calculate(4, 5)
goes on top - Inside
calculate
, console goes on top, followed byadd(4, 5)
console
finishes and gets removedadd
finishes and gets removedcalculate
finishes and gets removedconsole
insideprintTotal
goes on topprintTotal
finishes and gets removed
This happens very fast, but it’s still one thing at a time. This step-by-step process ensures that functions are executed in the correct order, maintaining an organised and predictable flow of execution.

The Problem with Synchronous Code
Let’s understand why asynchronous JavaScript exists in the first place.
Imagine we’re building a web app that needs to fetch user data from an API. If JavaScript worked only synchronously, our code might look like this:
console.log("Starting app...");
let userData = fetchUserData(); // This takes 3 seconds
console.log("User data:", userData);
console.log("App ready!");
The problem? The entire webpage would freeze for 3 seconds while waiting for the API response. Users couldn’t click buttons, scroll, or do anything. This creates a terrible user experience.
Why Blocking is Bad
When JavaScript runs long operations like API calls, file uploads, or complex calculations, the main thread gets blocked. This means:
- Users can’t interact with your website
- Animations stop working
- The browser becomes unresponsive
- Users think your site is broken
This is where asynchronous callbacks come to the rescue.
Asynchronous Callbacks – The Solution
Asynchronous callbacks allow us to handle time-consuming tasks without freezing the browser. Instead of waiting for an operation to complete, we tell JavaScript: “Start this task, and when it’s done, call this function.”
Here’s how the same API call would work with async callbacks:
console.log("Starting app...");
fetchUserData((userData) => {
console.log("User data:", userData);
});
console.log("App ready!");
Now the output would be:
Starting app...
App ready!
User data: [object Object]
Notice how “App ready!” appears before the user data? That’s because the app doesn’t wait for the API call to finish.
How JavaScript Stays Single-Threaded
The secret lies in the browser’s architecture. While JavaScript itself runs on one thread, browsers provide additional components that work together:
- Web APIs – Handle async operations (timers, network requests, DOM events)
- Callback Queue – Stores completed callbacks waiting to run
- Event Loop – Manages when callbacks get executed
These components work together to handle asynchronous operations without blocking the main thread.
Web APIs
Web APIs are built into browsers and handle the heavy lifting for async operations. Common Web APIs include:
setTimeout
andsetInterval
(timers)fetch
andXMLHttpRequest
(network requests)- DOM events (clicks, scrolls, form submissions)
setImmediate
(immediate execution)
These APIs work in the background while our JavaScript code continues running. When they finish, they put callbacks in the appropriate queue.
#1: How Does setTimeout Work?
This is probably the most asked question about asynchronous JavaScript. Let’s break it down:
console.log("Number 1");
setTimeout(() => {
console.log("Number 2");
}, 2000);
console.log("Number 3");
Output:
Number 1
Number 3
Number 2

Why doesn’t “Number 2” come after “Number 1”? Here’s what happens:
console.log("Number 1")
runs immediatelysetTimeout
starts a timer but doesn’t block the codeconsole.log("Number 3")
runs immediately- After 2 seconds, “Number 2” finally prints
The key insight: setTimeout
doesn’t pause your code. It schedules something to happen later.
Where Does the Callback Go? – Callback Queue
When setTimeout
finishes counting, it doesn’t immediately run your callback. Instead, it puts the callback in a waiting line called the “Callback Queue (Task Queue).”
Callback Queue holds asynchronous callbacks like setTimeout
, DOM events, etc., waiting to be executed. When the call stack is empty, the event loop picks the next function from the queue and pushes it onto the stack.
The Event Loop – JavaScript’s Traffic Controller
The event loop is like a traffic controller. It constantly checks:
- Is the call stack empty?
- Are there any callbacks waiting in the callback queue?
- If yes to both, move the first callback to the call stack
This happens thousands of times per second, but it follows this exact pattern every time.

#2: What Happens with setTimeout(fn, 0)?
This question trips up many developers:
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
console.log("End");
Even with 0 milliseconds, you get:
Start
End
Timeout
Why? Because setTimeout
always puts callbacks in the queue, even with 0 delay. The callback must wait for the call stack to be empty.
Promises and Microtasks – The VIP Queue
ES6 introduced Promises, which work differently from setTimeout
. They use a special “VIP queue” called the microtask queue.
These operations use the job queue (microtask queue) to run their callbacks, which have a higher priority than those in the task queue (macrotask queue). The event loop always processes microtasks first before moving on to the task queue.
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");
Output:
Start
End
Promise
Timeout
Notice how “Promise” comes before “Timeout“? That’s because the microtask queue has higher priority than the regular callback queue.

The event loop checks queues in this order:
- Call stack (synchronous code)
- Microtask queue (Promises, async/await)
- Callback queue (setTimeout, DOM events)
#3: Multiple setTimeout Calls
If you have multiple setTimeout(fn, 0)
calls in a row, how will they be executed in relation to other synchronous code?
console.log("Start");
setTimeout(() => console.log("Timer 1"), 0);
setTimeout(() => console.log("Timer 2"), 0);
setTimeout(() => console.log("Timer 3"), 0);
console.log("End");
Output:
Start
End
Timer 1
Timer 2
Timer 3
All timers go to the same callback queue and execute in order, but only after all synchronous code finishes.
#4: What About Nested Promises?
console.log("Start");
Promise.resolve()
.then(() => {
console.log("Promise 1");
return Promise.resolve();
})
.then(() => {
console.log("Promise 2");
});
setTimeout(() => {
console.log("Timeout");
}, 0);
console.log("End");
Output:
Start
End
Promise 1
Promise 2
Timeout
Each .then()
creates a new microtask, but they all run before any macrotasks.
#5: Error Handling in Async Code
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.reject("Error!")
.catch(() => {
console.log("Caught error");
});
console.log("End");
Error handling with .catch()
also uses the microtask queue.
#6: Order of Execution Challenge
Try to predict the output:
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve()
.then(() => {
console.log("C");
setTimeout(() => console.log("D"), 0);
});
setTimeout(() => console.log("E"), 0);
console.log("F");
Answer:
A
F
C
B
E
D
Remember: synchronous code first, then microtasks, then macrotasks.
Conclusion
Asynchronous JavaScript might seem complex, but it follows predictable rules. The event loop, queues, and Web APIs work together to keep your applications responsive.
Remember the key principle: JavaScript runs one thing at a time, but browsers provide tools to handle multiple operations without blocking your code.