Recommended Free Tools
To flatten nested promise logic, move dependent asynchronous steps into an async function and await each result where the next step needs it. Return the final value. The async function still returns a promise, so its caller must await that promise or handle its result and rejection.
Rewrite nested .then() calls as a straight-line sequence
A nested callback often starts another asynchronous operation before the first callback has finished:
getUser(id).then((user) => {
getPermissions(user.id).then((permissions) => {
render(user, permissions);
});
});
In an async function, await each dependent operation and use its result in the next step:
async function showUser(id) {
const user = await getUser(id);
const permissions = await getPermissions(user.id);
return render(user, permissions);
}
const result = await showUser("123");
The second call cannot be made until the first returns the user, so the sequential awaits express the dependency directly. Returning render(...) makes its result the async function’s result.
#1 Best Overall
If you keep a .then() chain instead, return the inner promise from the callback so the outer chain remains connected. MDN recommends keeping simple promise chains flat rather than nesting them; its promise guide also demonstrates the equivalent async/await style.
Understand what await and async return
await pauses the current async function’s continuation until its operand settles; it does not block the JavaScript main thread. When the promise fulfills, the await expression evaluates to its fulfillment value. When it rejects, the rejection reason is thrown at that expression. See MDN’s documentation for await and async functions.
Rank #2
An async function call always returns a promise, even if the function returns an ordinary value. Returning a promise from the function does not make its result synchronously available to the caller. The caller must await it or attach a handler, for example:
async function getLabel() {
return "ready";
}
const label = await getLabel();
Promise resolution also assimilates promises and thenables returned from an async function: the caller’s promise adopts their eventual state rather than receiving a promise as its fulfilled value. This is not the same as recursively flattening arrays or arbitrary objects. await handles a promise or thenable result; it does not walk data structures. MDN explains thenable assimilation in its Promise constructor reference content.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Choose sequential awaits or concurrent work by dependency
Use sequential awaits when a later operation needs an earlier result:
const user = await getUser(id);
const permissions = await getPermissions(user.id);
If operations are independent, start both before awaiting their combined result with Promise.all:
Rank #4
const [user, settings] = await Promise.all([
getUser(id),
getSettings(id),
]);
The choice is about dependencies and timing, not syntax preference: sequential code starts the second expression after the first await resumes, while Promise.all aggregates the results of the supplied promises or thenables. Consider its rejection behavior and whether you need a different settlement policy; MDN documents the available Promise static helpers.
Handle rejections at the right boundary
An awaited rejection throws inside the async function, so ordinary try/catch can recover from it or add context. If the function does not need local recovery, let its returned promise reject and handle the failure at the caller:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesBest Value
async function loadProfile(userId) {
const response = await fetch(`/users/${userId}`);
const user = await response.json();
return fetchPermissions(user.id);
}
try {
const profile = await loadProfile("123");
} catch (error) {
console.error("Could not load profile", error);
}
Use return await when the local function needs the rejection to occur inside a local try/catch or finally. Otherwise, returning the final promise directly is generally sufficient:
Quick Recap
async function loadPermissions(user) {
return fetchPermissions(user.id);
}
Common pitfalls when flattening promise code
- Forgetting the outer promise: calling an async function does not immediately give you its completed value. Await its result or attach fulfillment and rejection handlers.
- Awaiting every return mechanically: await when you need the value locally or need a local error-handling boundary; otherwise, return the promise directly.
- Confusing promise flattening with data flattening: promise resolution adopts nested thenables, but it does not recursively flatten arrays or object properties.
- Treating every thenable as inert data: an object with a
thenproperty can be assimilated byawait, even if it is not a native Promise. Be cautious when handling untrusted objects with customthenmethods.
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.




