JavaScript Promises
Master asynchronous operations with promises, chaining methods, async/await syntax, and parallel combinators.
TL;DR
- 01Use
Promiseinstances to manage deferred asynchronous values. - 02Chain
then(),catch(), andfinally()handlers to process values. - 03Leverage
asyncandawaitfor synchronous-looking promise code.
Tips
- 01Run independent asynchronous processes concurrently using
Promise.all()to prevent blocking call pipelines. - 02Utilize
Promise.allSettled()when you need results from all operations, including individual rejections.
Warnings
- 01Forgetting to return a value inside a
then()block breaks the promise chaining sequence. - 02A single rejected promise in
Promise.all()rejects the entire collection immediately without waiting.
Creating Promises
new Promise()Constructs a promise wrapper by passing resolve and reject callback handles.
const p = new Promise((resolve, reject) => {
if (success) resolve(data);
else reject(new Error("Failed"));
});Promise.resolve()Returns an already-fulfilled promise containing the supplied argument.
Promise.resolve(42)
.then(val => console.log(val));Promise.reject()Returns an already-rejected promise containing the supplied error reason.
Promise.reject(new Error("Failed"))
.catch(err => console.error(err));Handling Results
.then()Attaches a fulfillment handler to react when a promise resolves.
promise.then(result => {
console.log("Resolved:", result);
});.catch()Attaches a rejection handler to catch errors thrown in chains.
promise.catch(error => {
console.error("Caught error:", error);
});.finally()Attaches a callback that executes regardless of success or failure outcomes.
promise.finally(() => {
console.log("Operation completed");
});Promise Chaining
Chaining .then()Passes the return value of each handler to the next link.
fetch("/api/users/1")
.then(res => res.json())
.then(user => console.log(user.name))
.catch(err => console.error(err));Sequential waitingReturns a new promise inside then to pause subsequent step execution.
getUser(id)
.then(user => getOrders(user.id))
.then(orders => console.log(orders));Error recoveryAttaches a catch block to supply fallback values and continue chaining.
fetch("/api/data")
.catch(() => getCachedData())
.then(data => render(data));Combining Promises
Promise.all()Waits for all promises to resolve, rejecting instantly if any fail.
const [a, b] = await Promise.all([p1, p2]);Promise.race()Returns the result of the first promise to settle, resolving or rejecting.
const fastest = await Promise.race([p1, p2]);Promise.any()Returns the first successfully resolved promise, ignoring rejections.
const firstOk = await Promise.any([p1, p2]);Promise.allSettled()Waits for all promises to settle and returns their status array.
const results = await Promise.allSettled([p1, p2]);Common Patterns
Parallel executionInitiates independent promises simultaneously to speed up total execution times.
const p1 = fetchUser();
const p2 = fetchPosts();
const [user, posts] = await Promise.all([p1, p2]);Promisifying callbacksWraps a legacy callback function like setTimeout in a promise.
const delay = ms => {
return new Promise(r => setTimeout(r, ms));
};
await delay(1000);In Practice
Loads user profiles and order histories concurrently using parallel fetch requests to minimize user interface load delay.
- 01Initiate the user fetch request without awaiting its resolution.
- 02Kick off the order fetch request simultaneously in the background.
- 03Combine both promises using
Promise.allto await their concurrent completion. - 04Parse the JSON content of both resolved responses in parallel.
- 05Catch any network or parsing error using a
try-catchwrapper.
async function getDashboardData(userId) {
try {
const userPromise = fetch(`/users/${userId}`);
const ordersPromise = fetch(`/orders/${userId}`);
const [userRes, ordersRes] = await Promise.all([
userPromise,
ordersPromise
]);
return {
user: await userRes.json(),
orders: await ordersRes.json()
};
} catch (err) {
console.error("Dashboard failed to load", err);
return null;
}
}FAQ
Instantiate a new Promise((resolve, reject) => { ... }) wrapper. Call the resolve callback upon success. Invoke the reject callback with an error object if the operation fails.
Each await statement blocks execution until that specific promise settles. Sequentially awaiting independent operations increases execution times. Wrap them in Promise.all() to trigger parallel execution.
Unhandled rejections can crash Node.js processes or trigger console warnings in browsers. Always attach a .catch() block. Alternatively, wrap your asynchronous expressions in try-catch structures.
Promise.race() resolves or rejects as soon as the first input promise settles. Promise.any() waits for the first successful resolution, ignoring intermediate rejections. It throws an AggregateError if all fail.
No, a forEach loop is not designed to await asynchronous callbacks. The loop completes before the async callbacks finish executing. Use a standard for...of loop to ensure sequential execution.