Async Code in Node.js: Callbacks and Promises
What are we gonna Study?
Hey there folks! Hope you are doing great in your life and enjoying your dev life...
Ever wondered why to we write asynchronous code?
Ever thought that if callbacks allowed us to write asynchronous code, then why do we write it in Promises?
If yes, you've came to the right place...
So, we are basically going to study about these topics:
Why async code exists in Node.js
Callback-based async execution
Problems with nested callbacks
Promise-based async handling
Benefits of promises
Gather all your focus right here, and bear with me till the very end of it, I bet you would master every bit of it.
Why Asynchronous Code Exists in NodeJS
See, sometimes we have to perform some kind of operations which take time, and the JavaScript Engine never stops for those operations
let data = null;
fetch("https://example.com")
.then(res => data = res);
console.log(data); // Output: null (The engine did not stop to wait for the fetch)
Now here comes the asynchronous way of writing the code into the picture.
It basically allows you to do the asynchronous job in parallel...
Now, I hope now you understand why you need Asynchronous code in NodeJS, the reason is simple because several tasks like DB calls, reading files, uploading files, fetching data, etc requires time...
Callback Based Async Execution
The callback way is the oldest way to write Asynchronous code, let me tell you how to use it.
First of all, let's take 4 functions which are asynchronous.
function loginUser(email, password, onSuccess, onFailure) {
setTimeout(() => {
console.log("Checking credentials...");
if (email === "email@example.com" && password === "password") {
onSuccess({ id: 1, name: "Ved" });
} else {
onFailure("Login failed: Invalid credentials");
}
}, 1000);
}
function getUserProfile(userId, callback) {
setTimeout(() => {
console.log("Fetching profile for ID:", userId);
callback({ id: userId, bio: "Software Developer", location: "Mumbai" });
}, 800);
}
function getUserPosts(profileId, callback) {
setTimeout(() => {
console.log("Fetching posts for profile:", profileId);
callback([
{ id: 101, title: "Understanding Callbacks" },
{ id: 102, title: "JavaScript Engines" }
]);
}, 800);
}
function getPostComments(postId, callback) {
setTimeout(() => {
console.log("Fetching comments for post:", postId);
callback(["Keep it up!", "This helped a lot.", "Can you explain Async/Await?"]);
}, 500);
}
There were not actually hard if you read them properly...
Now, lets learn how to actually make our code asynchronous in such a way that we successfully utilize it.
loginUser("email@example.com", "password", (user) => {
console.log("Logged in:", user.name);
getUserProfile(user.id, (profile) => {
console.log("Profile loaded:", profile.bio);
getUserPosts(profile.id, (posts) => {
console.log("Posts fetched:", posts.length);
const firstPost = posts[0];
getPostComments(firstPost.id, (comments) => {
console.log("Comments for first post:", comments);
// If you had more steps, the nesting would continue...
}, (err) => console.error(err)); // Error handling is a nightmare
}, (err) => console.error(err));
}, (err) => console.error(err));
}, (err) => console.error(err));
Now this is the piece of code, I know i have gave a lot of code just in first time, but don't worry I'll explain the why behind it :)
Here, each function depends on a callback and each callback is nothing more than other function right?
This is how we wrote asynchronous code way back then, passing callbacks inside a function.
Problems With Nested Callbacks
Nested callbacks, just like we did above are known as Pyramid of Doom or Callback Hell, and the answer is pretty obvious:
The callback method isn't readable at all.
Error handling is a nightmare in callbacks way.
The worst part, it becomes more complex as you add more methods.
Do you think you can work with callbacks?
Technically Yes! you can but your code would be error prone, not readable, and very complex right?
That's the problem that promises solve.
Promise-Based Async Handling
Those 4 functions now change a little bit here
function loginUser(email, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (email === "email@example.com" && password === "password") {
resolve({ id: 1, name: "Ved" });
} else {
reject("Login failed: Invalid credentials");
}
}, 1000);
});
}
function getUserProfile(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, bio: "Software Developer" });
}, 800);
});
}
function getUserPosts(profileId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ id: 101, title: "Promises are great" }]);
}, 800);
});
}
function getPostComments(postId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(["Great post!", "Clear explanation."]);
}, 500);
});
}
Now, see this time, here is a new thing, that is Promise keyword,
It basically tells JavaScript that it is a promise and promises take two arguments (though we used only one) which are resolve and reject.
If you want to learn more about it, I'll put a link in the last section of the blog.
Now comes the utilization part, you are likely saying it's almost the same right?
loginUser("email@example.com", "password")
.then(user => getUserProfile(user.id))
.then(profile => getUserPosts(profile.id))
.then(posts => getPostComments(posts[0].id))
.then(comments => console.log("Final Comments:", comments))
.catch(err => {
console.error("Error caught in the chain:", err);
});
See, how easy it was, we just used multiple .then() to perform different operations with the data and .catch() for error handling.
Now please i am telling it again this blog is for the concept, not the code, I'll give the links of the practical code examples + explaining each keyword blogs in the end.
Benefits of Promises
Now you already know the benifits, let's list them together :)
Promises are simpler way of writing Asynchronous Code.
Your code is no longer messy.
Your code is now readable.
Error handling can simply be done by a
.catch(). (funfact: It can be done by.then()also but for that you have to read my promises blog.
The developers who were coding in JavaScript even before promises were introduced, took a deep breath of relief after their release.
Wrap Up!
That is it for this blog folks, really happy that you made it till the very end of it.
As promised here are the links of the blogs:
Go ahead read them out, You'll probably get some knowledge that you didn't had before...
I'll catch you up in the next blog, until then keep coding and enjoying your dev life. 💝

