By Rahul Lankeppanavar
JavaScript (JS) runs on a single thread, processing code sequentially, one line at a time. Yet many real-world tasks like reading files, querying databases, or fetching data from a server take longer than simple tasks. If JS waited for each of these tasks to finish before moving on, web pages and servers would freeze.
For example, lets make an simple GET
request to an API
let response = fetch("https://jsonplaceholder.typicode.com/todos/1");
console.log("Todos: ", response);
The above code doesn't log the data because fetch which is a longer running task has not finished processing yet so it doesn't return an value immediately rather it returns a Promise
.
The Promise
is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
You can think of it like lending money to a friend they "promise" to pay you back later. You don’t get the cash immediately, you wait until they fulfill (or break) that promise.
A Promise
object can be created using Promise()
constructor, which takes an executor function which receives two arguments resolve
and reject
. Calling resolve(value)
marks the promise as fulfilled and sets its result to value
, while calling reject(reason)
marks it as rejected with the given reason
.
const myPromise = new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
if (success) {
resolve("Operation succeeded!");
} else {
reject("Operation failed!");
}
});
A Promise
object has three key components:
Promise
, has three states
pending
(initial)fulfilled
(after resolve
)rejected
(after reject
)resolve
or reason passed to reject
.then()
, .catch()
, and .finally()
that run when the promise settles.Special chaining methods are used to handle Promise
results, each accepting a callback.
.then(onFulfilled)
- runs when the promise is fulfilled.catch(onRejected)
- runs when the promise is rejected.finally(onSettled)
- runs after either outcomemyPromise
.then((value) => {
console.log("Then:", value);
})
.catch((error) => {
console.error("Catch:", error);
})
.finally(() => {
console.log("Finally: Cleanup or follow‑up tasks");
});
console.log("This logs before the Promise settles.");
Output
This logs before the Promise settles.
Then: Operation succeeded! // or Catch: Operation failed!
Finally: Cleanup or follow‑up tasks
Here, the console.log()
outside the promise runs immediately, demonstrating that promises handle asynchronous operations without blocking the main thread.
async/await
offers a more readable, "synchronous" way to work with Promises. To use them first a function is marked as async
and await
is used before a Promise
to pause until it settles.
Let's take the same GET
API example
async function getTodos() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
console.log("Todo: ", response.json());
} catch (error) {
console.error("Erros: ", error);
}
}
getTodos();
console.log("This logs before the async function finishes.");
Here, await fetch(...)
pauses inside getTodo()
until the network call completes, so the console.log()
in the next line also waits until the Promise
is resolved. Errors bubble into the catch block.
Under the hood, JS engines (like V8 in Chrome or Node.js) use several components for async behavior.
setTimeout
, HTTP requests, file I/O etc.setTimeout
, DOM events etc..then()
, .catch()
, .finally()
), results of async/await
and micro task.Consider the below code snippet
console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 100);
Promise.resolve().then(() => console.log("Promise then"));
console.log("End");
Output
Start
End
Promise then
Timeout callback
Let's understand how JS executes the above code step by step
setTimeout
to Callback Queue.Promise
object is immediately resolved and its .then()
callback is added to Microtask Queue.Note : The delay passed to setTimeout
is the delay before its callback is placed on the Callback Queue not a guaranteed wait before its execution.
async/await
, and the event loop.async/await
makes Promise‑based code read like synchronous code.For a deeper dive into JavaScript’s asynchronous model, check out: