When you have several async tasks, you choose how to run them.
Sequential: one after another
Use this when each step needs the result of the previous one, or when the order matters, or when you must not overload a server.
const wait = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
async function sequential() {
const start = Date.now();
const a = await wait(200, "A");
const b = await wait(200, "B");
const c = await wait(200, "C");
console.log(a + b + c); // → ABC
console.log("elapsed ms:", Date.now() - start); // about 600
}
sequential();
Each await finishes before the next starts. Total time: about 600 ms (the sum).
Parallel: all at once
When tasks are independent, start them all first, then wait for all of them.
const wait = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
async function parallel() {
const start = Date.now();
const [a, b, c] = await Promise.all([wait(200, "A"), wait(200, "B"), wait(200, "C")]);
const elapsed = Date.now() - start;
console.log(a + b + c); // → ABC
console.log("elapsed ms:", elapsed); // about 200
}
parallel();
Total time: about 200 ms, the slowest one.
The trap: await inside a loop
const wait = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
const ids = [1, 2, 3];
async function slow() {
const results = [];
for (const id of ids) {
results.push(await wait(100, "user " + id)); // each waits for the previous: sequential
}
console.log(results);
}
slow();
If the requests are independent, this wastes time. The parallel version:
const wait = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
const ids = [1, 2, 3];
async function fast() {
const results = await Promise.all(ids.map(id => wait(100, "user " + id)));
console.log(results); // → ["user 1", "user 2", "user 3"]
}
fast();
ids.map(...) starts all three promises right away, and Promise.all waits for them.
Another way: start first, await later
const wait = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
async function main() {
const userPromise = wait(100, "user"); // started now
const postsPromise = wait(100, "posts"); // started now, runs at the same time
const user = await userPromise;
const posts = await postsPromise;
console.log(user, posts); // → user posts
}
main();
forEach does not wait
// Does NOT wait for the awaits inside it
ids.forEach(async id => { await load(id); });
// Use for...of (sequential) or Promise.all with map (parallel)
Limiting concurrency
Firing 500 requests at once can overwhelm a server or your connection. Process them in batches (a few at a time), or with a small helper or library.
How to choose
| Situation | Approach |
|---|---|
| Step B needs the result of step A | sequential (await each) |
| Independent tasks, all needed | parallel with Promise.all |
| Many tasks, polite to the server | batches of a limited size |
| Strict order of side effects | sequential |
A quick check
await several times in a row and none of the calls uses the previous result, you can probably run them in parallel.