JavaScript promises are a way to handle asynchronous operations. A Promise
is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
A promise can be in one of the following three states:
- Pending: Initial state, neither fulfilled nor rejected.
- Fulfilled: The operation completed successfully.
- Rejected: The operation failed.
A promise is created using the new Promise
constructor, which takes a function as its argument. This function is called with two arguments: resolve
and reject
.
const myPromise = new Promise((resolve, reject) => {
// Perform some asynchronous operation
const success = true; // Simulating success/failure
if (success) {
resolve("Operation Successful");
} else {
reject("Operation Failed");
}
});
You consume a promise using the .then()
and .catch()
methods.
.then()
is used to handle a fulfilled promise..catch()
is used to handle a rejected promise.
myPromise
.then(result => {
console.log(result); // Output: "Operation Successful"
})
.catch(error => {
console.log(error); // If rejected, would output: "Operation Failed"
});
The finally()
method is executed once the promise is settled, regardless of whether it was resolved or rejected.
myPromise
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log("Promise completed");
});
const asyncTask = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Task finished after 2 seconds");
}, 2000);
});
};
asyncTask()
.then(result => console.log(result)) // Output after 2 seconds: "Task finished after 2 seconds"
.catch(error => console.log(error));
You can chain multiple .then()
calls to handle multiple asynchronous operations sequentially.
const fetchData = () => {
return new Promise((resolve) => {
setTimeout(() => resolve("Data fetched"), 1000);
});
};
const processData = (data) => {
return new Promise((resolve) => {
setTimeout(() => resolve(`${data} and processed`), 1000);
});
};
fetchData()
.then(data => processData(data))
.then(result => console.log(result)) // Output after 2 seconds: "Data fetched and processed"
.catch(error => console.log(error));
Promise.all()
takes an array of promises and returns a new promise that resolves when all the input promises are resolved, or rejects as soon as one of the promises is rejected.
const p1 = Promise.resolve("Promise 1 resolved");
const p2 = new Promise(resolve => setTimeout(() => resolve("Promise 2 resolved"), 1000));
const p3 = Promise.resolve("Promise 3 resolved");
Promise.all([p1, p2, p3])
.then(results => console.log(results)) // Output after 1 second: ["Promise 1 resolved", "Promise 2 resolved", "Promise 3 resolved"]
.catch(error => console.log(error));
Promise.race()
returns a promise that resolves or rejects as soon as one of the promises in the array resolves or rejects.
const p1 = new Promise(resolve => setTimeout(() => resolve("Promise 1 finished first"), 1000));
const p2 = new Promise(resolve => setTimeout(() => resolve("Promise 2 finished second"), 2000));
Promise.race([p1, p2])
.then(result => console.log(result)) // Output after 1 second: "Promise 1 finished first"
.catch(error => console.log(error));
Promise.allSettled()
returns a promise that resolves after all of the given promises have either resolved or rejected, and returns an array of objects describing the outcome of each promise.
const p1 = Promise.resolve("Resolved");
const p2 = Promise.reject("Rejected");
Promise.allSettled([p1, p2])
.then(results => console.log(results));
// Output:
// [
// { status: "fulfilled", value: "Resolved" },
// { status: "rejected", reason: "Rejected" }
// ]
Promise.any()
returns the first fulfilled promise, ignoring rejected ones. It rejects only if all promises are rejected.
const p1 = Promise.reject("Promise 1 rejected");
const p2 = new Promise(resolve => setTimeout(() => resolve("Promise 2 resolved"), 1000));
Promise.any([p1, p2])
.then(result => console.log(result)) // Output after 1 second: "Promise 2 resolved"
.catch(error => console.log(error)); // This will not run because p2 resolves.
The async/await syntax is built on top of promises and allows for even cleaner code when handling asynchronous operations.
async function handleData() {
try {
const data = await fetchData();
const processedData = await processData(data);
console.log(processedData);
} catch (error) {
console.error(error);
}
}
handleData();
These static methods quickly create resolved or rejected promises, often useful in testing or forcing specific promise states.
Promise.resolve("Quick resolve").then(console.log); // Output: Quick resolve
Promise.reject("Quick reject").catch(console.log); // Output: Quick reject
Errors in promises can be handled by the .catch()
method, or by passing an error handler to the .then()
method.
const errorProneTask = () => {
return new Promise((resolve, reject) => {
reject("Something went wrong");
});
};
errorProneTask()
.then(result => console.log(result))
.catch(error => console.log(error)); // Output: "Something went wrong"
Alternatively, you can add an error handler inside .then()
:
errorProneTask()
.then(result => console.log(result), error => console.log(error)); // Output: "Something went wrong"
Promises provide a clean and efficient way to handle asynchronous tasks in JavaScript. By using promises, you can avoid the infamous "callback hell" and write more readable, maintainable code.