Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

async/await makes promise-based JavaScript easier to read, but it does not automatically run calls in parallel or limit how many operations run at once. Start independent work before awaiting it, choose a promise combinator for the outcome you need, and use a limiter when a large batch must stay within a safe number of active tasks.

What async and await do

An async function always returns a promise. Inside it, await pauses that function until the awaited value settles; it does not pause the entire JavaScript program. Other asynchronous work can continue while the function is waiting. See MDN’s guide to using promises.

Where you place await affects when a call starts. These statements start the calls sequentially: the second call begins only after the first has finished.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const first = await fetchFirst();
const second = await fetchSecond();

If both operations are independent and you need both results, start them before awaiting their combined result:

const [firstResult, secondResult] = await Promise.all([
  fetchFirst(),
  fetchSecond(),
]);

If the second operation needs the first operation’s result, keep them sequential. Promise coordination does not make synchronous, CPU-heavy JavaScript execute in parallel; actual parallel execution requires facilities such as worker threads.

Which promise combinator should you use?

Method When it settles Use it when Failure and cancellation behavior
Promise.all(iterable) Fulfills after every input fulfills; rejects as soon as an input rejects. Every result is required and any failure should reject the aggregate. A rejection does not cancel other operations already underway. Fulfillment values are in input order.
Promise.allSettled(iterable) After every input settles. You need each task’s success or failure recorded. Fulfills with outcome objects marked fulfilled or rejected, in input order. It does not cancel tasks.
Promise.any(iterable) When an input first fulfills, or after all inputs reject. Any one successful result is enough. It ignores rejections until a fulfillment; if all inputs reject, it rejects with an AggregateError. It does not cancel remaining operations.
Promise.race(iterable) As soon as an input fulfills or rejects. The earliest outcome is decisive, such as in a timeout race. The first rejection can win. The race does not cancel losing operations.

These methods coordinate promise outcomes; they do not create threads. For more detail on their behavior, consult MDN’s promise guide.

Does Promise.race cancel the other promises?

No. Promise.race() settles its returned promise when the first input settles, but the other operations continue unless they have their own cancellation mechanism. The same principle applies when Promise.any() fulfills early or Promise.all() rejects early: an aggregate result is not a cancellation signal.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For cancellable operations such as fetch, use AbortController to stop the underlying request when appropriate. A race alone can report that a timeout won while the request continues consuming resources.

How to limit concurrency for a large batch

Promise.all(items.map(doWork)) calls doWork for every item while building the array. With a large batch, that can start more operations than a service or runtime should handle at once. A concurrency limiter queues work and caps the number of active calls.

Use p-limit

The p-limit project documents this pattern:

import pLimit from 'p-limit';

const limit = pLimit(5);
const results = await Promise.all(
  items.map(item => limit(() => doWork(item)))
);

The value 5 is an example, not a generally recommended setting. Choose a cap in light of the external service’s limits, memory use, latency, and the work being performed. The limiter controls how many functions execute concurrently; Promise.all still decides how their returned promises are aggregated.

Other p-limit controls

p-limit also documents limit.map(iterable, mapper) as a convenience form, along with active and pending task counts. It is a focused limiter; the project points to p-queue for a fuller queue abstraction with more options and controls. Check the documentation for the installed package version because its API and behavior can evolve.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What to know about failures and stopping queued work

When one task fails

If an input makes Promise.all() reject, other tasks that have already started keep running. If continuing them is undesirable, handle or cancel that work explicitly with mechanisms supported by the operations.

When clearing a p-limit queue

p-limit’s clearQueue() discards tasks waiting to start; it does not cancel tasks already running. With the default rejectOnClear: false, promises for discarded pending tasks may remain unresolved if the caller is waiting for them. Account for that behavior when designing shutdown or error-handling paths.

When nesting limited work

Do not have a function occupying a slot in a limiter wait for an inner task that must acquire a slot from that same limiter. The outer tasks can occupy every slot and leave the inner tasks unable to start. Use a separate limiter for nested work.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.