Lesson 68 of 81
Two independent `await`s back to back run one after the other by default. Kicking them both off before awaiting either turns two round-trips into one.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Inside a Server Component, await is just JavaScript — it pauses the function until the promise settles. If you write two unrelated fetches with an await in front of each one, the second fetch doesn't even start until the first one finishes, even though nothing about it depended on the first one's result. You've turned two independent network calls into a relay race for no reason.
The fix isn't a Next.js feature — it's plain Promise.all. Start every fetch you can start immediately, collect the promises, and only await once you actually need the data.
// app/products/[id]/page.tsx
async function getProduct(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`);
return res.json();
}
async function getReviews(id: string) {
const res = await fetch(`https://api.example.com/products/${id}/reviews`);
return res.json();
}
async function getRelated(id: string) {
const res = await fetch(`https://api.example.com/products/${id}/related`);
return res.json();
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
// All three requests fire on the same tick — none of them
// waits on another.
const [product, reviews, related] = await Promise.all([
getProduct(id),
getReviews(id),
getRelated(id),
]);
return (
<article>
<h1>{product.name}</h1>
<ReviewList reviews={reviews} />
<RelatedProducts items={related} />
</article>
);
}
Compare that to writing const product = await getProduct(id); then const reviews = await getReviews(id); then const related = await getRelated(id); one after another — same result, but the total wait time becomes the sum of all three requests instead of the slowest one.
Promise.all rejects on the first failure and cancels nothing else. If getReviews throws, Promise.all rejects immediately with that error — but getProduct and getRelated keep running in the background; their results are just discarded. If you need partial results even when one fetch fails, use Promise.allSettled instead and check each result's status.fetch deduplication and caching (a separate mechanism) can still apply to each of these calls independently; the two ideas are complementary, not the same feature.ProductPage renders a child Server Component that does its own await fetch(...), that child still can't start until React begins rendering it — which is a different problem with a different fix.There's no Next.js API here — this is the same Promise.all pattern you'd use fetching data in any async JavaScript function, on the server or in the browser. What's new is where it matters: Server Components make it easy to write what looks like a simple list of sequential awaits, and because there's no visual loading spinner reminding you requests are serialized, the sequential version is easy to ship by accident.