1.Introduction
JavaScript is a single-threaded language, which means it can do only one task at a time. But if that’s true, how does it handle things like API calls, timers, or file loading without freezing the entire application?
That’s where Synchronous and Asynchronous programming comes in.
In this blog, we’ll break down:
What synchronous and asynchronous code means
Why JavaScript “doesn’t wait”
How it actually works behind the scenes
Before Going into the synchronous and asynchronous You are all having one question which is why Javascript is a single-threaded language?
JavaScript is single-threaded because:
It has one call stack, meaning it can execute only one task at a time.
But WHY was it designed like this?
1. Simplicity (Main Reason)
When JavaScript was created (by Brendan Eich):
It was meant to run inside the browser
Mainly to handle:
Button clicks
Form validation
Simple UI interactions
So, making it single-threaded:
Keeps things simple
Avoids complex issues like thread synchronization
JavaScript is single-threaded because it uses a single call stack to execute code one task at a time. This design simplifies execution and avoids concurrency issues like race conditions, while asynchronous behavior is handled using the event loop and browser APIs
A Simple Program for Single Thread Behaviour
console.log("Match Started");
function batting() {
console.log("Batsman started batting");
for (let i = 0; i < 1e9; i++) {} // playing long innings
console.log("Batsman finished batting");
}
function bowling() {
console.log("Bowler started bowling");
}
batting();
bowling();
console.log("Match Ended");
Output
Match Started
Batsman started batting
Batsman finished batting
Bowler started bowling
Match Ended
Explanation
First, batsman plays completely
Only after that, bowler starts bowling
Both don’t happen at the same time
One after another execution
Just like in cricket where batting must finish before bowling begins in this example, JavaScript executes one task at a time. Even if multiple functions are called, the next task waits until the current one finishes, proving that JavaScript is single-threaded
Here is Where Synchronus and Asynchronus Comes to play
2.What is Synchronous in JavaScript?
Before going into the technical part of synchronus we shall understand the synchronus non technically
Imagine you are eating chocolates 🍫
You eat one chocolate
Only after finishing it → you take the next chocolate
You don’t eat 2 chocolates at the same time
Likewise in Javascript
Computer also does the same:
It does one work
Waits till it finishes
Then does the next work
Now you get a idea of what synchronus actually do if we deep dive into the technical part you can clearly understand about this topic
JavaScript executes code line by line using a single call stack, where each operation must complete before the next one starts.
Single Thread + Call Stack
JavaScript is single-threaded, so it uses:
One Call Stack (Execution Stack)
Example
function starter() {
console.log("Starter is served 🍲");
}
function mainCourse() {
console.log("Main course is served 🍛");
}
function dessert() {
console.log("Dessert is served 🍰");
}
starter();
mainCourse();
dessert();
Output
Starter is served 🍲
Main course is served 🍛
Dessert is served 🍰
Explanation:
First, you eat starter 🍲
Then main course 🍛
Finally dessert 🍰
You don’t eat everything at the same time
One after another → synchronous
Blocking Nature
Synchronous code is blocking
Example
console.log("Start");
function heavyTask() {
for (let i = 0; i < 1e9; i++) {} // long work
console.log("Heavy Task Done");
}
heavyTask();
console.log("End");
What happens?
JS starts execution
Enters heavyTask()
Stays there until loop finishes
Only then moves to next line
Everything else is blocked
Execution Context
Whenever a function runs:
JavaScript creates an Execution Context
Each context has:
Memory (variables)
Code execution phase
Flow:
Global Execution Context created first
Then function contexts are created and pushed to stack
Real-World Impact
Problems with synchronous code:
UI freezing
Slow performance
Poor user experience
Example:
Loading large data
Heavy calculations
File processing
*The Problems in Synchronus is Solved in Asynchronus So lets deep dive into Asynchronus Concept *
3.What is Asynchronous JavaScript
Asynchronous execution means:
JavaScript can delegate long-running tasks and continue executing other code, instead of blocking the main thread.
How JavaScript Actually Handles Async
JavaScript runtime consists of:
Call Stack
Web APIs (Browser / Node APIs)
Callback Queue (Task Queue)
Event Loop
console.log("Match Started 🏏");
setTimeout(() => {
console.log("Drinks break over 🍹, match resumes");
}, 2000);
console.log("First over is going on...");
Output
Match Started 🏏
First over is going on...
Drinks break over 🍹, match resumes
Explanation
Match starts
First over continues immediately
Meanwhile:
- Drinks break is scheduled ⏳ (setTimeout)
JavaScript does NOT wait
It continues the match
After 2 seconds:
- Drinks break message comes
What This Shows
Even though drinks break was set:
Match didn’t stop
Other things continued
This is asynchronous behavior
Event Loop (Heart of Async)
The event loop continuously checks:
Is Call Stack empty?
If yes → take task from queue → execute
This is why JS feels non-blocking
Types of Async Tasks
Macro Tasks (Callback Queue)
setTimeout
setInterval
DOM events
Micro Tasks (Priority Queue)
Promises
queueMicrotask
Microtasks run before macrotasks
Okay this will lead up to the methods of asynchronus in Javascript
4.Methods Used in Asynchronous JavaScript
In JavaScript, async behavior is achieved using different techniques (methods):
What is a Promise in JavaScript
A Promise in JavaScript is an object that represents the eventual completion or failure of an asynchronous operation, allowing developers to handle results using methods like then, catch, and finally.
Basic Structure of a Promise
const promise = new Promise((resolve, reject) => {
// async work
});
It has 2 important functions:
resolve() → success
reject() → failure
Promise States
A Promise has 3 states:
Pending → Initial state
Fulfilled → Success (resolve called)
Rejected → Failed (reject called)
const foodOrder = new Promise((resolve, reject) => {
let foodReady = true;
if (foodReady) {
resolve("Your food is ready ");
} else {
reject("Sorry, food is not available ");
}
});
foodOrder
.then(message => console.log(message))
.catch(error => console.log(error));
Output (if foodReady = true)
Your food is ready
Output (if foodReady = false)
Sorry, food is not available
Explanation
You order food
Kitchen checks:
If food is ready → serve it (resolve)
If not → say unavailable (reject)
Customer (your code):
.then() → handles success
.catch() → handles failure
A Promise in JavaScript is like ordering food—either the food gets delivered successfully (resolve) or the order fails (reject), and we handle both outcomes using then and catch.
Promise Chaining
You can chain multiple .then()
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve(10), 1000);
});
}
getData()
.then(num => num * 2)
.then(num => num + 5)
.then(result => console.log(result)); // 25
Each .then() passes value to next
async / await(To Be Discussed)
fetch() API(To Be Discussed)
Top comments (0)