When people study asynchronous JavaScript, Promise and async/await show up almost immediately. The syntax can look approachable, but without understanding why they exist, the code quickly becomes confusing again.
async/await makes asynchronous code look more sequential, which improves readability, but it does not turn asynchronous behavior into truly synchronous execution underneath.
This post covers three things.
- what Promise is
- why
async/awaitexists - how both differ from callback-heavy code
The key idea is this: Promise represents a value that will complete later, and async/await is syntax that makes Promise-based code easier to read.
What Promise is
A Promise is a JavaScript object that represents a result that does not exist yet but will exist later.
For example, a network request does not return data immediately. A Promise lets code describe:
- still waiting
- completed successfully
- failed
and attach follow-up logic once the result is ready.
Why callbacks became awkward
Before Promise became common, asynchronous follow-up logic was often written with nested callbacks.
fetchUser(userId, function (user) {
fetchOrders(user.id, function (orders) {
saveLog(orders, function () {
console.log('done');
});
});
});
As this grows, the nesting becomes hard to read and error handling becomes fragmented.
Promise improved that by making continuation flow more chainable.
How to read Promise chains
The classic way is through .then() and .catch().
fetchUser(userId)
.then((user) => fetchOrders(user.id))
.then((orders) => saveLog(orders))
.catch((error) => console.error(error));
This is cleaner than deeply nested callbacks, but long chains can still become tiring to scan.
Why async/await exists
async/await exists to make Promise-based logic read more like a top-to-bottom sequence.
async function run() {
try {
const user = await fetchUser(userId);
const orders = await fetchOrders(user.id);
await saveLog(orders);
} catch (error) {
console.error(error);
}
}
This tends to feel more natural because the reader can follow the steps in order.
Does async/await make code synchronous
Not really, and this is an important beginner point.
await pauses the local async function flow until the Promise settles, but the overall mechanism is still asynchronous and Promise-based underneath.
So async/await is better thought of as syntax that makes async behavior easier to read rather than syntax that turns it into ordinary blocking code.
When Promise chains or async/await fit better
In many everyday cases, async/await is easier to read. But Promise utilities are still very useful.
For example:
- long sequential flow ->
async/awaitoften reads better - combining several async operations ->
Promise.all()may feel more natural
So these are not really enemies. async/await sits on top of Promise behavior.
Common mistakes
1. Thinking await automatically improves performance
await is mainly a control-flow and readability tool.
2. Awaiting independent tasks one by one
If tasks do not depend on each other, waiting sequentially may be unnecessary.
const [user, posts] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
]);
3. Ignoring error handling
Whether using Promise chains or async/await, failure paths still need deliberate design.
A good learning exercise
- start with a callback example
- rewrite it as a Promise chain
- rewrite it again with
async/await - group independent work with
Promise.all()
That comparison teaches the concepts much faster than memorizing syntax alone.
FAQ
Q. Does an async function always return a Promise?
Yes. Even a normal return value is wrapped in a Promise.
Q. Can await be used anywhere?
Usually it is used inside an async function.
Q. If I learn Promise well, can I skip async/await?
It is better to see them together. async/await becomes much clearer once Promise itself is understood.
Read Next
- If you want to understand how these async steps are actually scheduled by the runtime, continue with Event Loop Guide.
- If you want to revisit the larger conceptual difference first, Synchronous vs Asynchronous Guide is a helpful companion.
While AdSense review is pending, related guides are shown instead of ads.
Start Here
Continue with the core guides that pull steady search traffic.
- Middleware Troubleshooting Guide: Redis vs RabbitMQ vs Kafka A practical middleware troubleshooting guide for developers covering when to reach for Redis, RabbitMQ, or Kafka symptoms first, and which problem patterns usually belong to each tool.
- Kubernetes CrashLoopBackOff: What to Check First A practical Kubernetes CrashLoopBackOff troubleshooting guide covering startup failures, probe issues, config mistakes, and what to inspect first.
- Kafka Consumer Lag Increasing: Troubleshooting Guide A practical Kafka consumer lag troubleshooting guide covering what lag usually means, which consumer metrics to check first, and how poll timing, processing speed, and fetch patterns affect lag.
- Kafka Rebalancing Too Often: Common Causes and Fixes A practical Kafka troubleshooting guide covering why consumer groups rebalance too often, what poll timing and group protocol settings matter, and how to stop rebalances from interrupting useful work.
- Docker Container Keeps Restarting: What to Check First A practical Docker restart-loop troubleshooting guide covering exit codes, command failures, environment mistakes, health checks, and what to inspect first.
While AdSense review is pending, related guides are shown instead of ads.