Introduction
Promises in JavaScript are a powerful tool for managing asynchronous operations, allowing developers to write cleaner, more manageable code.
What is a Promise?
A Promise is an object representing the eventual completion or failure of an asynchronous operation. A Promise holds a value that might be available now, later, or never. It represents the idea of promising to do something. Promises have three states:
Pending: Initial state, neither fulfilled nor rejected.
Fulfilled: The operation completed successfully.
Rejected: The operation failed.
Consider the following example where a Promise resolves another Promise:
new Promise((resolveOuter) => {
resolveOuter(
new Promise((resolveInner) => {
setTimeout(resolveInner, 1000);
})
);
});
In this case, the outer Promise is pending until the inner Promise resolves. The completion of the inner Promise after 1 second fulfills the outer Promise.
Chaining Promises
Promises can be chained to perform sequential asynchronous operations. Methods like .then()
, .catch()
, and .finally()
enable this chaining:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 300);
});
myPromise
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejected)
.finally(() => console.log('Operation completed.'));
.then()
executes a callback on Promise fulfillment or rejection..catch()
is a shorthand for handling rejections..finally()
executes after the Promise settles, regardless of its outcome.
Handling Multiple Promises
JavaScript provides several methods to deal with multiple Promises concurrently:
Promise.all()
waits for all promises to be resolved or for any to be rejected. Helpful for combining the outcomes of several promises.Promise.all([promise1, promise2]).then((results) => { const [result1, result2] = results; console.log(result1, result2); });
Promise.allSettled()
waits for all promises to settle, regardless of the outcome. Each promise's result is provided, indicating success or failure.Promise.allSettled([promise1, promise2]).then((results) => results.forEach((result) => console.log(result.status)));
Promise.race()
waits for the first promise to settle, either fulfilled or rejected. This is useful for timeout patterns.Promise.race([promise1, promise2]).then((result) => console.log(result));
Promise.any()
waits for the first promise to fulfill, ignoring all rejections unless all promises are rejected.Promise.any([promise1, promise2]).then((result) => console.log(result));
Async/Await: Syntactic Sugar for Promises
Async/await syntax offers a cleaner, more readable way to work with Promises, making asynchronous code appear synchronous:
async function fetchData() {
try {
const data = await fetch('https://api.example.com/data');
console.log(await data.json());
} catch (error) {
console.error('Failed to fetch data:', error);
}
}
An
async
function implicitly returns a Promise.The
await
keyword pauses the function execution until the Promise settles.Use
try/catch
blocks to handle potential rejections.
Concurrent Execution with Async/Await
To run Promises concurrently within an async function, utilize Promise.all()
:
async function fetchMultipleData() {
try {
const [dataA, dataB] = await Promise.all([fetchDataA(), fetchDataB()]);
console.log(dataA, dataB);
} catch (error) {
console.error('Failed to fetch data:', error);
}
}
This approach ensures that dataA
and dataB
are fetched in parallel, optimizing performance.