Why await Doesn't Make JavaScript Run in Order
Learn why await only pauses one async function—not the whole program—with a two-function example, log timeline, and when Promise.all() is better.

A lot of developers learn async/await and walk away with one idea:
I used
await, so everything waits.
That sentence is almost true — and the "almost" is where the bugs live.
await pauses one async function. It does not pause the rest of your program. Timers still fire. Event handlers still run. Other async functions keep moving. JavaScript is still taking turns.
Once you see that, a lot of "why did this log first?" moments stop being mysterious.
The assumption
Here's the mental model that gets people in trouble:
await doSomething();
// nothing else in the entire program can happen until this finishesThat would be convenient. It is also not how JavaScript works.
await is local. It only tells this function to pause at this line and come back later. Everything else in the program is free to keep running.
A short surprising example
Two functions. One setTimeout. A few console.log calls.
function later(label, ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(label);
resolve();
}, ms);
});
}
async function loadUser() {
console.log('loadUser: start');
await later('loadUser: done', 1000);
console.log('loadUser: after await');
}
async function loadSettings() {
console.log('loadSettings: start');
await later('loadSettings: done', 200);
console.log('loadSettings: after await');
}
console.log('program: start');
loadUser();
loadSettings();
console.log('program: after both calls');If await froze the whole program, you would expect something like this:
loadUserstarts- the program waits a full second
loadUserfinishesloadSettingsstarts- then the rest of the logs appear
That is not what happens.
What actually logs, and when
Here is the timeline:
0ms program: start
0ms loadUser: start
0ms loadSettings: start
0ms program: after both calls
200ms loadSettings: done
200ms loadSettings: after await
1000ms loadUser: done
1000ms loadUser: after awaitloadUser() hits await and steps aside. loadSettings() starts immediately. The next line in the main script runs immediately too. A fifth of a second later, the faster timer wins — even though we called the slower function first.
The order of the function calls is not the order of the finish times.
What await actually pauses
An async function always returns a Promise. When that function hits await, three things happen:
- The current function pauses at that line.
- JavaScript records "come back here when this Promise settles."
- Control returns to whoever called the function.
That last part is the one people miss.
async function loadUser() {
await later('loadUser: done', 1000);
// this line waits
}
loadUser();
// this line does not waitloadUser() is paused. The caller is not.
If you want the caller to wait, the caller has to await too:
await loadUser();
await loadSettings();Now loadSettings() will not start until loadUser() finishes. That is sequential on purpose — because you chained the waits, not because await stopped the whole program.
How other code keeps running
While loadUser() is paused, JavaScript is still doing work.
In the example above, that work is a setTimeout. In a real app, it is usually one of these:
- another
asyncfunction you already started - a click handler
- a
fetch()that finishes first - a React/Svelte effect
- the next line after you forgot to
await
You do not need a deep event-loop lecture to use this well. One picture is enough:
Call loadUser()
→ hits await
→ loadUser is parked
→ JavaScript looks for other work
→ loadSettings() runs
→ timers and network calls keep ticking
→ whichever Promise finishes first gets to resumeawait is a bookmark, not a freeze frame.
When sequential awaits are right
Sometimes you do want one thing to finish before the next thing starts. That is what sequential awaits are for.
Use them when the second call needs the first result:
async function loadDashboard(userId) {
const user = await fetchUser(userId);
const projects = await fetchProjects(user.teamId);
return { user, projects };
}You cannot fetch the projects until you know the team id. Waiting in order is the correct design.
The same rule applies to writes that must happen in sequence:
await createCustomer(form);
await sendWelcomeEmail(form.email);If the customer row is not there yet, the email step should not run.
When Promise.all() is better
If the tasks do not depend on each other, waiting in a line wastes time.
This version looks tidy and is often slower than it needs to be:
async function loadPage() {
const user = await fetchUser();
const settings = await fetchSettings();
const notifications = await fetchNotifications();
return { user, settings, notifications };
}Each request sits around until the previous one finishes. If each call takes 300ms, you pay about 900ms.
These three requests do not need each other. Start them together:
async function loadPage() {
const [user, settings, notifications] = await Promise.all([
fetchUser(),
fetchSettings(),
fetchNotifications()
]);
return { user, settings, notifications };
}Now the slowest request sets the wait time, not the sum of all three. If they all take 300ms, you wait about 300ms.
That is the same idea as the first example. loadUser() and loadSettings() overlapped because we started both without waiting. Promise.all() makes that overlap explicit — and it gives you the results in a predictable array when every Promise has finished.
One caveat: Promise.all() rejects as soon as any Promise rejects. If you want each request to succeed or fail on its own, use Promise.allSettled() instead.
A rule of thumb you can remember
await pauses this function. It does not pause JavaScript.
If the next line needs the result, await it.
If two tasks can run at the same time, start both, then await Promise.all().
If you called an async function and did not await it, you only started it. The rest of your code will keep going.
That last sentence is the whole article in one line. The surprising logs are just that idea showing up on a timeline.
Related posts
Comments (0)
Join the discussion and share your thoughts on this post.
No comments yet
Be the first to share your thoughts!
