Skip to main content

Async Code in Node.js: Callbacks and Promises

Updated 3 min readMarkdown

1) : Why async code exists in Node.js

Lets imagine you are in a class with 20 other persons and if you and anyone have to go outside the they have to solve a question that takes the time what should everyone have to do stand on the gate and when each person turn comes then solve question and go or solve on the own desk and when you solved then go outside .

The second condition seems favorable because only one gate is in the class and it is easy to every student not to block other until he solves the question .

So as we imagined in the above case their is same in the NodeJS their is a single threaded and if their is queues of tasks and if each task blocks other tasks until they finished their is no time efficiency and it would take so much time . So we use same concepts that we prefer in above we runs them into background then when they completed they added to the queues that they can run without any inefficiency .

2) : Callback-based async execution

**Asynchronous Callback :-**Callbacks are functions passed into other functions as arguments, executed once an asynchronous task completes. Node.js uses an error-first callback pattern where the first argument is reserved for an error object.

The Mechanism :-

  1. Initiation :- The main Thread calls the asynchronous function .

  2. Registration :-Node.js passes the task to the system kernel or a background thread pool (libuv).

  3. Execution: The main thread immediately moves to the next line of code without waiting.

  4. Trigger: Once the background task finishes, the callback function is pushed to the event queue and executed on the main thread

Problems with nested callbacks -

Nesting multiple asynchronous operations inside each other creates deeply nested, unreadable, and hard-to-maintain code blocks, often referred to as the "Pyramid of Doom."

// A classic visual representation of Callback Hell
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getYetMoreData(c, function(d) {
        console.log("Finally got the data: " + d);
      });
    });
  });
});

Promise-based async handling :-

Promises were introduced to solve callback nesting by wrapping asynchronous actions inside an object wrapper. A Promise represents a value that may be available now, in the future, or never .

Chaining: Multiple asynchronous operations can be linked sequentially using consecutive .then() blocks, keeping the structure flat.

// Much cleaner, completely flat structure
db.users.findById(userId)
  .then(user => db.orders.getLatest(user.id))
  .then(order => db.payments.getDetails(order.paymentId))
  .then(payment => fs.promises.writeFile(`invoice.txt`, payment.summary))
  .then(() => console.log("Invoice generated!"))
  .catch(handleError); // One catch block handles all errors above

Benefits of promises :-

  • Flattens code Block .

  • Centralized Error handling .

  • Immutable state .

  • Async/Await support .

  • Better Composability .

No comments yet. Be the first to comment.