Free tools Windows power users keep installed
One-click scans. No signup required.
async and await make Promise-based code easier to read, but they do not change the underlying Promise model. Use sequential await when one operation depends on another; start independent operations together and choose a Promise combinator according to how failures and results should be handled. For errors, recover deliberately or let rejection reach the caller. For cancellation, signal the underlying operation—racing it against a timer does not stop it.
What async and await do
An async function always returns a Promise. Its returned value becomes that Promise’s fulfillment value; an exception that escapes the function makes the Promise reject. This remains true when the function returns an ordinary value rather than explicitly constructing a Promise. MDN’s async function reference describes this behavior.
await accepts a Promise, a thenable, or an ordinary value. If the awaited Promise is pending, the current async function’s continuation waits for it to settle. On fulfillment, the expression evaluates to its value; on rejection, the rejection reason is thrown at the await expression, where normal try/catch can handle it. Await does not block the main thread while the operation is pending: other JavaScript work can proceed. MDN’s await reference explains the syntax and behavior; the ECMAScript 2024 specification defines the underlying Await operation.
async function loadProfile(url) {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
This example illustrates the control flow: await the response, then return the result of parsing it. The response check is an application-level choice about what counts as success for this function.
#1 Best Overall
Decide whether operations depend on each other
Keep dependent work sequential
If a later call needs data returned by an earlier one, await the first result before making the second call:
const user = await getUser();
const orders = await getOrders(user.id);
The second call needs user.id, so this is a real dependency. Starting both calls together would not provide the required input.
Start independent work together
If operations do not depend on one another, call both before awaiting their combined result:
Rank #2
const [profile, settings] = await Promise.all([
getProfile(),
getSettings(),
]);
Both calls start before the aggregate is awaited, allowing their asynchronous work to overlap. By contrast, await getProfile() followed by await getSettings() delays the second call until the first settles. This is concurrency, not parallel execution of JavaScript code: a given JavaScript thread executes one task at a time. Worker threads can provide parallel execution. See MDN’s Promise reference for the distinction and combinator behavior.
Recommended Free Tools
Put concurrency inside a branch when appropriate
Branches can use the same dependency test. Start related independent work together only in the branch that needs it; keep a call sequential if its input comes from an earlier result.
async function loadDashboard(includeSettings) {
const user = await getUser();
if (includeSettings) {
const [orders, settings] = await Promise.all([
getOrders(user.id),
getSettings(user.id),
]);
return { user, orders, settings };
}
const orders = await getOrders(user.id);
return { user, orders };
}
Here, the user lookup must finish before either branch can use the user ID. Within the first branch, the orders and settings calls are independent and can be started together.
Choose a Promise combinator by its outcome policy
These methods are not interchangeable speed helpers. Choose according to whether the feature needs every operation to succeed, can use one successful result, should stop caring after the first settlement, or must inspect every outcome.
| Method | When the aggregate settles | What it returns or does on rejection | Useful when |
|---|---|---|---|
Promise.all |
Fulfills after all inputs fulfill; rejects as soon as an input rejects. | On fulfillment, returns values in input order. A rejection rejects the aggregate; it does not itself cancel other operations. | Every result is needed and any failure should fail the overall operation. |
Promise.allSettled |
Waits until every input settles. | Returns an outcome for each input, identifying it as fulfilled or rejected. | Every result or failure must be inspected, even if some operations fail. |
Promise.any |
Fulfills when the first input fulfills; rejects if all inputs reject. | On fulfillment, returns the first fulfilled value. | Any one successful result is sufficient. |
Promise.race |
Settles as soon as the first input settles, whether fulfilled or rejected. | Adopts that first settlement’s value or rejection. | The first result of either kind should determine the aggregate. |
For example, use allSettled for independent background checks when the interface must report each check’s outcome rather than failing at the first rejection. Use all when a page cannot be considered ready without every required result. The choice is about product behavior as much as code structure.
Handle errors where recovery is possible
An awaited rejection behaves like a thrown error at that point in the function. Put a try/catch around the smallest region where you can meaningfully recover or add context. If there is no useful local recovery, let the async function reject so its caller can decide what to do.
Rank #4
async function getData() {
try {
return await fetchData();
} catch (error) {
throw new Error("Could not load data", { cause: error });
}
}
This pattern adds context while preserving the original error as its cause. The same rejection flow can be written with .then() and .catch(); async/await changes how the control flow is expressed, not the Promise’s error-propagation model. MDN’s guide to using promises covers chain error handling and cancellation limitations.
A catch that only logs and then returns can accidentally turn a failure into a fulfilled Promise with an unusable value such as undefined. Return a deliberate fallback only when it is valid for the caller; otherwise rethrow or allow the rejection to propagate.
Timeouts and cancellation are different
A Promise does not provide a universal built-in cancellation protocol. Cancellation must be supported by the underlying operation. Where an API accepts a cancellation signal, an AbortController can create an AbortSignal to pass into that operation; handle an abort through the same error flow as other operation failures.
Best Value
A timeout built with Promise.race only decides when the caller stops waiting for the aggregate result. It does not guarantee that the losing operation stops running. To stop work, cancellation must reach the underlying API through a signal or another cancellation mechanism that API supports.
Check the module context for top-level await
In ordinary scripts, await is valid inside an async function. Top-level await is available in JavaScript modules, not ordinary script context. Because this is a syntax-context distinction, a top-level await error may require changing how the file is loaded as a module or wrapping the work in an async function. Consult MDN’s await reference and async function reference for the relevant contexts.
Quick Recap
A practical decision checklist
- Does the next operation need the previous result? Await in sequence.
- Are the operations independent? Start them first, then await a suitable aggregate.
- Must every operation succeed? Consider
Promise.all. - Must every outcome be inspected, including failures? Use
Promise.allSettled. - Is one successful result enough? Consider
Promise.any. - Should whichever operation settles first determine the result? Use
Promise.race, but do not mistake it for cancellation. - Can this function actually recover from an error? Catch it locally only if recovery or useful context is possible; otherwise preserve the rejection.
- Does the operation support cancellation? Pass its supported signal or token; a Promise alone cannot stop it.
- Is
awaitat file scope? Confirm the file is running as a module.
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.




