Sequential vs Parallel Async Operations
When you have multiple async operations, the order matters. Sometimes each operation depends on the previous result — they must run sequentially. Sometimes they are independent — they can run in parallel, finishing much faster.
Sequential operations
Section titled “Sequential operations”await one after another runs them in series:
async function loadAll() { const rates = await fetchRates(); // waits ~500ms const expenses = await fetchExpenses(); // waits another ~300ms return { rates, expenses }; // total: ~800ms}The second await does not start until the first one resolves. This is correct when the second call depends on the first result — but wasteful when both are independent.
Parallel operations with Promise.all
Section titled “Parallel operations with Promise.all”Promise.all accepts an array of Promises and returns a single Promise that resolves when all of them resolve:
async function loadAll() { const [rates, user] = await Promise.all([ fetchRates(), // starts immediately fetchUserData(), // starts immediately, in parallel ]); return { rates, user }; // total: max(~500ms, ~300ms) = ~500ms}Both requests start at the same time. The combined wait is the duration of the slowest request, not the sum of all requests.
The result is an array of resolved values in the same order as the input array — destructure it directly.
Promise.all rejects fast
Section titled “Promise.all rejects fast”If any Promise in the array rejects, Promise.all immediately rejects with that error:
try { const [rates, extras] = await Promise.all([ fetchRates(), fetchOptionalData(), // if this rejects... ]);} catch (error) { // ...this catch runs immediately, even if fetchRates was still pending}This is fine when all the operations are equally required. When some are optional, handle them separately.
Promise.allSettled — when partial success is acceptable
Section titled “Promise.allSettled — when partial success is acceptable”Promise.allSettled waits for all Promises regardless of whether they fulfill or reject. It returns an array of result objects:
const results = await Promise.allSettled([ fetchRates(), fetchUserPreferences(),]);
for (const result of results) { if (result.status === 'fulfilled') { console.log('Got:', result.value); } else { console.warn('Failed:', result.reason.message); }}Use Promise.allSettled when you want partial results — load what you can, degrade gracefully for what fails.
A practical BudgetBuddy example
Section titled “A practical BudgetBuddy example”On startup, BudgetBuddy needs the exchange rates and any user preferences (display currency, categories). Both are independent:
async function init() { const [ratesResult, prefsResult] = await Promise.allSettled([ fetchRates(), loadUserPreferences(), ]);
const rates = ratesResult.status === 'fulfilled' ? ratesResult.value : { USD: 1 }; const prefs = prefsResult.status === 'fulfilled' ? prefsResult.value : defaultPrefs;
renderExpenses(loadExpenses(), rates, prefs);}Neither failure crashes initialization. The app uses safe defaults and renders.
When to choose sequential vs parallel
Section titled “When to choose sequential vs parallel”| Sequential | Parallel (Promise.all) |
|---|---|
| Each result is needed to start the next | Operations are independent |
| One request builds on the previous | Fastest possible combined response |
| Operations must happen in a specific order | Partial failure should crash everything |
Use allSettled when partial success is OK |
Exercise
Section titled “Exercise”- Write two stub functions that return Promises with artificial delays (
setTimeout-based). Time how long running them sequentially takes vs in parallel withPromise.all. - Demonstrate that
Promise.allrejects immediately if one Promise rejects — even if the other is still pending. - Use
Promise.allSettledwith one failing and one succeeding Promise — extract the successful value while handling the failure. - In BudgetBuddy’s
init(), usePromise.allSettledto load rates and user preferences in parallel with graceful fallbacks for each.
- Sequential
awaitruns operations one after another — correct when each depends on the previous result. Promise.all([...])runs all Promises in parallel — resolves when all succeed, rejects fast if any fail.Promise.allSettled([...])runs all in parallel — resolves when all settle, regardless of success or failure.- Use
allSettledwhen partial results are acceptable; useallwhen all results are required. - Always time your async code — sequential vs parallel can mean the difference between a 2-second load and a 0.5-second load.