Facebook Pixel

Running Callbacks in Parallel in JavaScript

Running multiple callbacks in parallel requires a counter. Here is how and why promises are better.

Running Callbacks in Parallel in JavaScript

Running multiple async operations in parallel with callbacks requires a manual counter. Promises make this much easier with Promise.all.

The Callback Way (Manual Counter)

function parallel(tasks, callback) { const results = []; let done = 0; let hasError = false; tasks.forEach((task, i) => { task((err, result) => { if (hasError) return; if (err) { hasError = true; callback(err); return; } results[i] = result; if (++done === tasks.length) { callback(null, results); } }); }); } parallel( [ (cb) => fetchUser(1, cb), (cb) => fetchUser(2, cb), (cb) => fetchUser(3, cb), ], (err, users) => { if (err) { handleError(err); return; } console.log(users); } ); `` This is verbose and error-prone. You must track: - How many tasks have completed (`done`). - Whether an error has occurred (`hasError`). - The order of results (`results[i]`). ### The Promise Way (Promise.all) ```js Promise.all([fetchUserP(1), fetchUserP(2), fetchUserP(3)]) .then((users) => { console.log(users); }) .catch((err) => { handleError(err); }); `` `Promise.all` runs all promises in parallel and waits for all to complete. If any rejects, the whole thing rejects. Results are in order. ### The Async/Await Way ```js async function main() { try { const users = await Promise.all([fetchUserP(1), fetchUserP(2), fetchUserP(3)]); console.log(users); } catch (err) { handleError(err); } } `` ### Promise.allSettled (Wait for All, Even Failures) ```js const results = await Promise.allSettled([ fetchUserP(1), fetchUserP(2), fetchUserP(3), ]); // results: [{ status: "fulfilled", value: ... }, { status: "rejected", reason: ... }, ...] `` `Promise.allSettled` waits for all promises, even if some reject. Each result has a `status` (`"fulfilled"` or `"rejected"`). ### The Takeaway Running callbacks in parallel requires a manual counter (verbose, error-prone). `Promise.all` runs promises in parallel and waits for all, with results in order. `Promise.allSettled` waits for all even if some fail. Use promises for parallel operations; avoid manual counters.

Use a manual counter: track how many tasks have completed, store results in order, and call the final callback when all are done. This is verbose and error-prone. Use Promise.all instead for a cleaner solution.

Use Promise.all([promise1, promise2, promise3]). It runs all promises in parallel and waits for all to complete. Results are in order. If any promise rejects, the whole thing rejects.

Promise.all rejects if any promise rejects (fast fail). Promise.allSettled waits for all promises, even if some reject, and returns an array of { status, value/reason } objects. Use allSettled when you want all results regardless of failures.

Yes. The results array is in the same order as the input promises, regardless of which promise resolves first. This is one advantage over the manual counter approach with callbacks.

Use await Promise.all([...]). Start all promises at once (do not await them individually), then await Promise.all to wait for all to complete. This is much faster than sequential await.

Ready to master React completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.