A Promise in JavaScript is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value. It is a placeholder for a value that may not be available yet but will be resolved at some point in the future. Promises provide a cleaner and more organized way to handle asynchronous operations compared to traditional callback-based approaches.
A Promise can be in one of three states:
Once a Promise is fulfilled or rejected, it is considered settled, and its state cannot change.
To create a Promise, you use the Promise
constructor, which takes a single argument: a callback function called the executor. The executor function takes two parameters: resolve
and reject
, which are functions used to change the state of the Promise.
const myPromise = new Promise((resolve, reject) => {
// Simulate an asynchronous operation
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber < 0.5) {
resolve(randomNumber); // Fulfill the promise
} else {
reject(new Error('Random number is greater than or equal to 0.5')); // Reject the promise
}
}, 1000);
});
In this example, we create a Promise that simulates an asynchronous operation using setTimeout
. After 1 second, we generate a random number. If the random number is less than 0.5, we fulfill the Promise with the random number. Otherwise, we reject the Promise with an error.
To consume a Promise, you use the then
and catch
methods. The then
method is called when the Promise is fulfilled, and it takes a callback function that receives the resolved value. The catch
method is called when the Promise is rejected, and it takes a callback function that receives the reason for the rejection.
myPromise
.then((result) => {
console.log('Promise fulfilled:', result);
})
.catch((error) => {
console.error('Promise rejected:', error.message);
});
In this example, if the Promise is fulfilled, the then
callback will be executed, and the resolved value will be logged to the console. If the Promise is rejected, the catch
callback will be executed, and the error message will be logged to the console.
One of the main advantages of Promises is the ability to chain them together. You can call the then
method on a Promise and return a new Promise from the callback function. This allows you to perform a series of asynchronous operations in a sequential manner.
function asyncOperation1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Async operation 1 completed');
resolve(1);
}, 1000);
});
}
function asyncOperation2(result) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Async operation 2 completed with result:', result);
resolve(result + 1);
}, 1000);
});
}
function asyncOperation3(result) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Async operation 3 completed with result:', result);
resolve(result + 1);
}, 1000);
});
}
asyncOperation1()
.then(asyncOperation2)
.then(asyncOperation3)
.then((finalResult) => {
console.log('Final result:', finalResult);
});
In this example, we have three asynchronous operations represented by functions that return Promises. We chain these operations together using the then
method. Each operation waits for the previous one to complete before starting.
When chaining Promises, errors can occur at any point in the chain. You can use a single catch
method at the end of the chain to handle all errors.
function asyncOperationWithError() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('An error occurred in async operation'));
}, 1000);
});
}
asyncOperationWithError()
.then((result) => {
console.log('This will not be executed:', result);
})
.catch((error) => {
console.error('Error caught:', error.message);
});
In this example, the asyncOperationWithError
function rejects the Promise after 1 second. The then
method will not be executed, and the catch
method will handle the error.
Promise.all
and Promise.race
are two useful methods for working with multiple Promises.
Promise.all
takes an array of Promises and returns a new Promise that is fulfilled when all of the input Promises are fulfilled or rejected as soon as one of the input Promises is rejected.
const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve(3), 3000));
Promise.all([promise1, promise2, promise3])
.then((results) => {
console.log('All promises fulfilled:', results);
})
.catch((error) => {
console.error('One or more promises rejected:', error);
});
In this example, the Promise.all
Promise will be fulfilled after 3 seconds when all of the input Promises are fulfilled. The resolved value will be an array containing the resolved values of all the input Promises.
Promise.race
takes an array of Promises and returns a new Promise that is settled as soon as one of the input Promises is settled. The resolved or rejected value of the returned Promise will be the same as the first settled input Promise.
const promise4 = new Promise((resolve) => setTimeout(() => resolve(4), 2000));
const promise5 = new Promise((resolve) => setTimeout(() => resolve(5), 1000));
Promise.race([promise4, promise5])
.then((result) => {
console.log('First promise settled:', result);
})
.catch((error) => {
console.error('First promise rejected:', error);
});
In this example, the Promise.race
Promise will be settled after 1 second when promise5
is fulfilled. The resolved value will be the value of promise5
.
catch
method to prevent unhandled rejections.async/await
: The async/await
syntax provides a more synchronous-looking way to write asynchronous code using Promises. It is built on top of Promises and makes the code even more readable.async function asyncFunction() {
try {
const result1 = await asyncOperation1();
const result2 = await asyncOperation2(result1);
const result3 = await asyncOperation3(result2);
console.log('Final result:', result3);
} catch (error) {
console.error('Error:', error.message);
}
}
asyncFunction();
JavaScript Promises are a powerful tool for handling asynchronous operations. They provide a cleaner and more organized way to write asynchronous code compared to traditional callback-based approaches. By understanding the fundamental concepts of Promises, how to create and consume them, and how to chain them together, you can write more robust and maintainable asynchronous code. Additionally, using best practices such as async/await
can further improve the readability of your code.