The problem promises solve
A promise represents work that is not finished yet. It starts pending, then settles exactly once as fulfilled or rejected. The important part is not only the value. It is the scheduling behavior around what happens after the value arrives.
Most confusion appears when promise callbacks are mixed with synchronous code, timers, and async functions. The result can look random until you separate the call stack, promise state, and microtask queue.
const promise = Promise.resolve("data");
console.log("sync start");
promise.then((value) => console.log(value));
console.log("sync end");
// sync start
// sync end
// dataPromise states are simple, scheduling is the tricky part
Pending means the promise has not settled. Fulfilled means it settled with a value. Rejected means it settled with a reason. A promise cannot move from fulfilled to rejected later.
The callback passed to then, catch, or finally does not run immediately when the line is created. It is scheduled as a microtask once the promise settles.
Try the idea
Callbacks are registered, but no continuation can run until the promise settles.
async/await is promise syntax with a pause point
An async function always returns a promise. The await keyword pauses the async function, not the entire JavaScript runtime. Other synchronous code continues, and the continuation after await is scheduled as a microtask.
That is why await makes code easier to read, but it does not remove the event-loop rules.
async function loadUser() {
const user = await fetchUser();
return user.name;
}
// loadUser() returns a Promise<string>Common mistakes
The common promise mistakes are usually scheduling mistakes: forgetting to return a promise from a then callback, catching too early, mixing unrelated concerns in one chain, or assuming await blocks the whole application.
Production notes for JavaScript apps
When this concept moves from a tutorial into a real app, the important question is not whether the example works once. It is whether the behavior is still predictable when network latency, retries, user interaction, rendering, and logging all happen together.
Keep the boundary visible. Name the queue, state, error, or ownership rule in the code so the next developer can understand why the behavior exists without reading the whole feature from top to bottom.
Conclusion
Promises become much easier when you stop asking whether the code is async and start asking where the continuation is scheduled.

