Sequential API requests in order
Lesson 9 of 11
9Sequential API requests in order
The big pictureYou send ten requests over the network and the fastest returns first, not the first one sent. Print each reply when it lands and the order scrambles. The fix is to separate "when it arrived" from "where it is shown".
What the picture shows: The request pipeline: the first request gives a list of links, each link is requested, and order is kept by the link's position, not by when its reply arrives.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
You send five friends to five shops with an ordered list. They come back at different times. Write down what each brings the moment they return and the list scrambles. Better to put each thing in its slot by shop number, then read the slots in order.
fetch returns a Promise. .map(url => fetch(url)) fires every request at once, and Promise.all waits for them and returns results in input order whatever the arrival time, failing entirely on the first rejection (Promise.allSettled does not). for (const u of urls) await fetch(u) is sequential: slower, but it respects rate limits. forEach with async waits for nothing, a common bug. To cap concurrency use a worker pool or a library like p-limit.
Before You Start
📨HTTP and JSON
You know requests, replies and the shape of JSON.
🟨Modern JavaScript
The front-end lesson: Promises and async/await.
The Core Idea
1Why the order scrambles
Here we simulate the network with timers of different delays, so no connection is needed. The third request is fastest, so it prints first:
const delays = { a: 30, b: 20, c: 5 };
const fakeFetch = (id) =>
new Promise((resolve) => setTimeout(() => resolve(`item ${id}`), delays[id]));
for (const id of ["a", "b", "c"]) {
fakeFetch(id).then((item) => console.log("arrived:", item));
}2Promise.all keeps the order
We fire all requests together and wait for them all, and the results come back ordered by input:
const delays = { a: 30, b: 20, c: 5 };
const fakeFetch = (id) =>
new Promise((resolve) => setTimeout(() => resolve(`item ${id}`), delays[id]));
async function main() {
const items = await Promise.all(["a", "b", "c"].map(fakeFetch));
items.forEach((item) => console.log(item));
for (const id of ["c", "b", "a"]) {
console.log("sequential:", await fakeFetch(id));
}
}
main();ids.forEach(async (id) => console.log(await fetch(id))) looks sequential but waits for nothing: forEach ignores the returned Promise. Use for...of or Promise.all.3Exercise: a real public API
Pick a free public API with no key that returns an object holding a list of links (say a film and the links to its characters), and write a Node script that takes a number from the command line, fetches the object, then prints each item's name in the list's order. Use the built-in fetch in Node 18+ and process.argv[2], and handle a non-200 reply.
// skeleton: fill in the two TODOs
const id = process.argv[2];
const res = await fetch(`${process.env.API_BASE}/films/${id}/`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const film = await res.json();
// TODO: fetch every link in film.characters, keeping the order
// TODO: print each name on its own line.mjs extension so top-level await works.Worked example
Problem: 50 links, and the API allows 10 requests a second. Analysis: Promise.all on all fifty breaks the limit and brings 429 errors, and sequential is slow. Fix: split the links into batches of 10, await Promise.all(batch) for each batch, then join the results in order. Reasonable speed, preserved order, and the limit respected.
Myth vs Reality
| Myth | Reality |
|---|---|
| Promise.all returns results in arrival order. | It always returns them in input order. |
| async inside forEach waits for each item. | forEach does not wait for the Promise. Use for...of. |
Test your understanding
In what order does Promise.all return its results?
Result i belongs to input i whatever its arrival time.
Which loop waits for each await in turn?
for...of inside an async function pauses the loop at each await.
One link fails inside Promise.all. What happens?
If you want successes alongside failures use Promise.allSettled.
Quick review cards
Check Your Understanding
What is the time difference between parallel and sequential?
Parallel takes the slowest request's time; sequential takes the sum of all.
Why is console.log inside then not enough?
Because it prints in arrival order, not list order.
How do you respect a rate limit?
With size-limited batches or a fixed-size worker pool.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Done with this lesson?
Sign in to save your progress.