Async code in node.js

Async/Await in JavaScript: Writing Cleaner Asynchronous Code
JavaScript wasn't built for waiting. It runs one thing at a time — so whenever your code needs to fetch data, read a file, or talk to a database, things get tricky. Over the years, we've gone from callbacks to promises, and eventually to something that actually feels natural: async/await.
1. Why async/await was introduced
Before async/await, we had promises. And before promises, we had callbacks. Both worked — but neither felt great to read or write.
Promises were a step up, but chaining .then() after .then() after .then() started to look like a ladder nobody wanted to climb. It wasn't uncommon to see code like this:
fetchUser()
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));
It works. But it doesn't read like normal code. It reads like a chain reaction.
Async/await was introduced in ES2017 to fix exactly this — to let you write asynchronous code that looks synchronous. Easier to read, easier to reason about.
2. How async functions work
An async function is just a regular function with one special power: it always returns a promise, even if you return a plain value from it.
async function greet() {
return "Hello!";
}
greet().then(msg => console.log(msg)); // Hello!
You didn't return a promise — JavaScript wrapped it in one for you. That's what makes async functions special. They're what people mean when they say async/await is syntactic sugar over promises. It's not a new system. It's the same promises underneath, just dressed up to look cleaner.
3. Await keyword concept
await is the other half of the pair. You use it inside an async function to pause execution until a promise resolves.
async function getUser() {
const response = await fetch("https://api.example.com/user");
const user = await response.json();
console.log(user);
}
Without await, you'd get a pending promise, not actual data. With it, your code waits right there — no callbacks, no .then() — and picks up once the value is ready.
One rule to remember: you can only use await inside an async function. Try using it outside and JavaScript will throw an error.
4. Error handling with async code
This is where a lot of people get tripped up. With promises, you use .catch(). With async/await, you use good old try/catch — the same thing you'd use for synchronous errors.
async function getUser() {
try {
const response = await fetch("https://api.example.com/user");
const user = await response.json();
console.log(user);
} catch (error) {
console.error("Something went wrong:", error);
}
}
If the fetch fails, the catch block runs. Clean, simple, and familiar.
You can also handle errors at the call site if you prefer:
getUser().catch(err => console.error(err));
Both approaches are valid. Pick whichever fits your code better.
5. Comparison with promises
Let's look at the same task written both ways, side by side.
With Promises:
function loadData() {
fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
}
With Async/Await:
async function loadData() {
try {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
The logic is identical. The difference is in how it reads. The async/await version looks like a step-by-step list of instructions. The promise version looks like a pipeline.
For simple cases, both are fine. But when the logic gets complex — multiple dependent calls, conditionals, loops — async/await wins every time on readability.
Wrapping Up
Async/await didn't reinvent the wheel. It just made the wheel easier to use. If you understand promises, you already understand what's happening under the hood. Async/await is just a cleaner way to write it.
Start using it in your next project. You'll wonder why you ever wrote .then() chains.




