JavaScript Synchronous vs. Asynchronous
JavaScript is single-threaded — it can only do one thing at a time.
But real programs need to handle things like network requests, timers, and file reads. If any of those blocked the thread while waiting, the page would freeze and nothing else could run.
That's the problem asynchronous JavaScript solves.
- What Is Synchronous
- What Is Asynchronous
- The Event Loop
- Common Async Operations
- Ways to Handle Async Code
What Is Synchronous
Synchronous code runs line by line, in order. Each line waits for the previous one to finish before moving on.
console.log("First");
console.log("Second");
console.log("Third");Output:
First
Second
ThirdStraightforward — but if one line takes a long time (like waiting for a server response), everything else has to wait. The whole thread is blocked until it's done.
What Is Asynchronous
Asynchronous code doesn't wait. It kicks off an operation, moves on, and comes back to handle the result when it's ready.
console.log("Start");
setTimeout(function () {
console.log("setTimeout fires");
}, 1000);
console.log("End");Output:
Start
End
setTimeout firessetTimeout doesn't block anything. JavaScript logs "End" immediately, then comes back to run the callback after 1 second.
The Event Loop
The mechanism that makes all of this work is the Event Loop.
JavaScript's runtime has a few moving parts:
- Call Stack — where synchronous code runs, one task at a time
- Web APIs — browser-provided features like
setTimeout,fetch, and DOM events that run in the background - Task Queue — once a Web API operation completes, its callback is placed here to wait
- Event Loop — continuously watches the Call Stack; when it's empty, it pushes the next task from the Task Queue in
Here's a concrete example:
console.log("A");
setTimeout(function () {
console.log("B");
}, 0);
console.log("C");Output:
A
C
BEven with a 0 delay, "B" runs last. The setTimeout callback is handed off to the Web APIs, then queued in the Task Queue. The Event Loop only pushes it onto the Call Stack once "A" and "C" have already run.
Common Async Operations
Most async work in JavaScript falls into a few categories:
setTimeout/setInterval— timersfetch— network requests- DOM events — user interactions like
clickandinput Promise— a wrapper for async operationsasync/await— cleaner syntax for working with Promises
// Timer
setTimeout(() => {
console.log("runs after 1 second");
}, 1000);
// Network request
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data));
// DOM event
button.addEventListener("click", () => {
console.log("button clicked");
});Ways to Handle Async Code
There are three main approaches. Each one was introduced to address the shortcomings of the one before it.
Callbacks
The original approach — pass a function as an argument, and call it when the operation is done:
function fetchData(callback) {
setTimeout(function () {
callback("data");
}, 1000);
}
fetchData(function (data) {
console.log(data); // "data"
});Works fine for simple cases. But nest a few of these together and you end up with Callback Hell:
fetchUser(function (user) {
fetchPosts(user.id, function (posts) {
fetchComments(posts[0].id, function (comments) {
console.log(comments);
});
});
});Hard to read, hard to maintain.
Promises
Introduced in ES6, Promises give async code more structure:
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));Chaining .then() calls is much cleaner than nested callbacks.
async / await
Introduced in ES2017, async/await lets you write async code that reads like synchronous code:
async function getData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
getData();await pauses the function until the Promise resolves — but it doesn't block the rest of the program. It can only be used inside an async function.
Conclusion
| Synchronous | Asynchronous | |
|---|---|---|
| Execution | Line by line, blocks until done | Non-blocking, handles result later |
| Use cases | General logic | Network requests, timers, I/O |
| Tools | — | Callbacks, Promises, async/await |
JavaScript's async model is what lets a single-threaded language handle real-world workloads without grinding to a halt. Understanding it is essential for writing JavaScript that actually works the way you expect.