# Async Code in Node.js: Callbacks and Promises

Overview

We all know that JavaScript is a single-threaded language, and due to that, when a time-consuming task like file read/write or making a web request can block the whole thread, we can encounter the [convoy](https://www.geeksforgeeks.org/operating-systems/convoy-effect-operating-systems/) effect.

To avoid problems, we have an event loop that smartly handles this task by creating a thread pool. Which help us to do multiple tasks simultaneously with only a single thread. And this behaviour is called Asynchronous behaviour. To do this in JavaScript, we need to write async code.

## Why async/await Was Introduced

Before the async/wait was introduced in JavaScript, developers mainly relied on Promises to handle asynchronous operations. Promises improved things compared to callbacks; still, they had some drawbacks:

Problems with Promises:

*   Chained .then() call reduces the code readability.
    
*   In the case of nested logic, the code becomes a message
    
*   Handling errors becomes messy
    
*   Debuging become hard
    

### Solution: async/await

Async/await was introduced (ES2017) as **syntactic sugar over promises**. It doesn’t replace promises—it makes them easier to use and read.

This allows developers to write asynchronous code like synchronus manner.

### How Async Functions Work

An `async` A function is simply a function that **always returns a promise**.

### Basic Syntax

```javascript
async function greet() {
  return "Hello";
}
```

Even though we return a string, JavaScript automatically wraps it inside a promise:

```javascript
greet().then(console.log); // "Hello"
```

Equivalent:

```javascript
function greet() {
  return Promise.resolve("Hello");
}
```

Key Point

*   Async Keyword make sure that the function always returns a promise.
    
*   You can only use await inside an async function
    
*   It simplifies promise handling.
    

### Await Keyword Concept

The `await` keyword is what makes async/await powerful.

It pauses the execution of the async function **until the promise is resolved or rejected**.

### Example

```javascript
async function getData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
}
```

### What’s happening?

1.  `fetch()` returns a promise
    
2.  `await` pauses execution until the promise resolves
    
3.  Once resolved, it gives the actual value
    
4.  Execution continues
    

### Important Note

`await` **does NOT block the entire JavaScript thread**

It only pauses execution **inside that async function**, while the event loop continues running other tasks.

### Error Handling with Async Code

One of the biggest advantages of async/await is **clean error handling** using `try...catch`.

### Using Promises

```javascript
fetchData()
  .then((data) => processData(data))
  .catch((err) => console.error(err));
```

### Using Async/Await

```javascript
async function run() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}
```

Observation

*   With async/await, the code readability and scalability both improved.
    
*   We can now centralize error handling.
    
*   It becomes easy to debug the code
    

### Key Differences

| Feature | Promises | Async/Await |
| --- | --- | --- |
| Readability | Medium | High |
| Error handling | `.catch()` | `try...catch` |
| Debugging | Harder | Easier |
| Structure | Chain-based | Sequential style |

## When NOT to Use Await

Async/await is great, but not always necessary.

### Parallel Execution Issue

```javascript
await task1();
await task2();
```

This runs tasks **sequentially**, not in parallel.

Better approach:

```javascript
await Promise.all([task1(), task2()]);
```

So use async/await wisely depending on your needs.

### Final Thoughts

Async/await is one of the best improvements in modern JavaScript. It makes asynchronous code:

*   Cleaner
    
*   More readable
    
*   Easier to debug
    
*   Closer to synchronous thinking
    

But remember:

*   It’s still based on promises
    
*   It doesn’t make code faster automatically
    
*   Understanding promises is still essential
