Sync vs Async JavaScript — Everything You Need to Know
JavaScript is a single-threaded, synchronous language at its core. But in the real world, it powers complex web apps that fetch data, read…
Sync vs Async JavaScript — Everything You Need to Know
JavaScript is a single-threaded, synchronous language at its core. But in the real world, it powers complex web apps that fetch data, read files, and handle user events — all without freezing the browser. How? Through asynchronous programming. Let’s break it all down.
Difference between Sync & Async JS
Synchronous code executes line by line. Each statement waits for the previous one to finish before running. This is simple and predictable — but problematic when a task takes time.
javascript
function sync() {
console.log("1");
console.log("2");
console.log("3");
}
sync();
// Output: 1, 2, 3
The issue? If one operation — say, fetching data from an API — takes 3 seconds, everything after it is blocked. The entire program becomes unresponsive.
Asynchronous code solves this. Long-running tasks are handed off to browser Web APIs or Node.js runtime APIs. Once complete, their results are queued and executed when the main thread is free.
javascript
function asyncExample() {
console.log("1");
setTimeout(() => {
console.log("2");
}, 2000);
console.log("3");
}
asyncExample();
// Output: 1, 3, 2
JavaScript doesn’t wait for setTimeout to finish — it moves on immediately, and "2" prints after 2 seconds.
What is a Callback & Callback Hell?
A callback is simply a function passed as an argument to another function, to be executed later.
javascript
function one(callback) {
console.log("one");
callback();
}
function two() {
console.log("two");
}
one(two);
// Output: one, two
Callbacks work well for simple cases. But when you chain multiple asynchronous operations, things get messy fast.
Callback Hell (also called the Pyramid of Doom) happens when callbacks are nested inside callbacks, creating deeply indented, hard-to-read code:
javascript
getUser((user) => {
console.log(user);
setTimeout(() => {
console.log(user.id);
setTimeout(() => {
console.log(user.name);
}, 1000);
}, 1000);
});
This structure is painful to read, debug, and maintain. This is one of the core problems that Promises were designed to solve.
What are Web Browser APIs?
Web APIs are features built into the browser that allow JavaScript to perform operations outside the main Call Stack — without blocking it.
Common examples:
setTimeout()andsetInterval()— delay or repeat executionfetch()— make HTTP requests- DOM APIs — manipulate the document
- History API — control browser navigation
- Location API — access geolocation
These APIs let JavaScript offload time-consuming work, keep the UI responsive, and handle results asynchronously when they’re ready.
What is a Promise & Its Different Methods?
A Promise is an object representing the eventual result of an asynchronous operation. It gives you a cleaner, more structured way to handle async code compared to callbacks.
A Promise has three states:
- Pending — the operation is still in progress
- Fulfilled — the operation completed successfully
- Rejected — the operation failed
javascript
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Success");
} else {
reject("Failed");
}
});
Promise Methods
**Promise.all()* — Waits for all* promises to resolve. If any single one rejects, it immediately rejects.
javascript
Promise.all([promise1, promise2]);
**Promise.allSettled()* — Waits for all* promises to settle, regardless of whether they resolved or rejected. Useful when you want results from every promise, even the failed ones.
javascript
Promise.allSettled([promise1, promise2]);
**Promise.race()* — Returns the result of whichever promise settles first* — resolved or rejected.
javascript
Promise.race([promise1, promise2]);
**Promise.any()* — Returns the first successfully resolved* promise. If all promises reject, it throws an AggregateError.
javascript
Promise.any([promise1, promise2]);
What is Inversion of Control?
When you pass a callback to a third-party API, you’re handing over control of your own code:
javascript
apiCall(myCallback);
Now you’re trusting that API to call your function correctly — at the right time, the right number of times. It might call it once, multiple times, or never at all. This loss of control over your program’s flow is called Inversion of Control.
Promises solve this by returning a value you control. Instead of trusting someone else to call your function, you decide what to do when the Promise resolves or rejects.
What is the Event Loop?
The Event Loop is the mechanism that makes asynchronous JavaScript possible. It continuously monitors the Call Stack and the task queues.
Here’s the priority order:
Call Stack → Microtask Queue → Callback Queue
- Call Stack — executes currently running code
- Microtask Queue — holds Promise callbacks (
.then,.catch) - Callback Queue — holds callbacks from Web APIs like
setTimeout
javascript
console.log("Start");
setTimeout(() => console.log("Timer"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
// Output:
// Start
// End
// Promise
// Timer
Even though setTimeout has a 0ms delay, the Promise callback runs first because the Microtask Queue has higher priority than the Callback Queue.
How do we use the fetch method?
fetch() is a built-in browser API for making HTTP requests. It returns a Promise, making it easy to integrate with .then() chains or async/await.
javascript
fetch("https://api.example.com/users")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));
Since most APIs return JSON, we use .json() to parse the response into a usable JavaScript object.
Common HTTP Methods
MethodPurposeGETRetrieve dataPOSTCreate or send new dataPUTReplace existing data entirelyPATCHPartially update existing dataDELETERemove data
Error Handling While Writing Async Code
Errors are inevitable in async operations — network failures, invalid responses, timeouts. Proper error handling keeps your app from silently breaking.
With Promises:
javascript
promise
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
With Callbacks (error-first pattern):
javascript
getUser((err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
With async/await (recommended):
javascript
async function getData() {
try {
const data = await promise;
console.log(data);
} catch (error) {
console.error(error);
}
}
async/await is the most readable approach — it makes asynchronous code look and behave like synchronous code, while try/catch keeps error handling clean and familiar.
Understanding these concepts — the Event Loop, Promises, callbacks, and async/await — is foundational to writing reliable JavaScript. Once these clicks, the rest of JS async patterns become much easier to reason about.
If you found this helpful, consider following for more JavaScript deep dives.
메타데이터
- post_id
- 2e7b7e8586da
- slug
- sync-vs-async-javascript-everything-you-need-to-know-2e7b7e8586da
- url
- https://medium.com/@architkamble001/sync-vs-async-javascript-everything-you-need-to-know-2e7b7e8586da
- canonical_url
- https://medium.com/@architkamble001/sync-vs-async-javascript-everything-you-need-to-know-2e7b7e8586da
- author_url
- https://medium.com/@architkamble001
- status
- ok
- fetched_at
- 2026-07-13 06:23:13