JavaScript promises are a cornerstone of modern web development, enabling efficient handling of asynchronous operations. This article will guide you through the essentials of JavaScript promises.
What is a Promise?
A JavaScript promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises are particularly useful for dealing with asynchronous tasks without getting bogged down in callback hell.
Let’s say in the below snippet, we are faking an API call, and after 1sec we get the data. But as soon as the code snippet executes, it will return us an object { data: undefined }
const apiCallData = new Promise((resolve, reject)=>{
const dataFromAPI = [1, 2, 3]
setTimeout(()=>{
resolve(dataFromAPI);
}, 1000);
});
apiCallData.then((apiData)=>{
// Do any thing here
});
After some time (async time of 1 second), this Promise Object will be filled with data.
{ data: [1, 2, 3] }
Now, once we have data in the Promise Object, the above callback(then) function
will be called automatically with the data.
apiCallData.then((apiData)=>{
// Once we have data in the Promise Object, this will be executed.
// Do any thing here
});
Promise Object
Promise State: A promise has three states:
- Pending: The initial state, neither fulfilled nor rejected.
- Fulfilled: The operation was completed successfully.
- Rejected: The operation failed.
Promise Result: response/ undefined
Basic Structure of Promise:
Here’s how you create a promise:
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
let success = true; // This is just a simulation
if (success) {
resolve("Operation was successful!");
} else {
reject("Operation failed.");
}
});
Using Promises: then
and catch
To handle the result of a promise, you use the then
method for success and the catch
method for errors.
promise
.then((message) => {
console.log(message); // Output: Operation was successful!
})
.catch((error) => {
console.error(error); // This will run if the promise is rejected
});
Chaining Promises
Promises can be chained to handle multiple asynchronous operations in a sequence.
const firstPromise = new Promise((resolve) => {
setTimeout(() => {
resolve("First promise resolved.");
}, 1000);
});
firstPromise
.then((message) => {
console.log(message);
return new Promise((resolve) => {
setTimeout(() => {
resolve("Second promise resolved.");
}, 1000);
});
})
.then((message) => {
console.log(message);
})
.catch((error) => {
console.error(error);
});
Handling Multiple Promises: Promise.all
and Promise.race
Promise.all
: It takes an array of promises and returns a single promise that resolves when all of the promises in the array resolve
, or rejects if any of the promises reject
.
const promise1 = Promise.resolve("Promise 1 resolved.");
const promise2 = Promise.resolve("Promise 2 resolved.");
const promise3 = Promise.resolve("Promise 3 resolved.");
Promise.all([promise1, promise2, promise3])
.then((messages) => {
console.log(messages);
// Output: [ "Promise 1 resolved.", "Promise 2 resolved.", "Promise 3 resolved." ]
})
.catch((error) => {
console.error(error);
});
Interviewers might ask to create a polyfill for this Promise.all method, this is one of the most asked interview questions.
Promise.race
: It returns a promise that resolves or rejects as soon as one of the promises in the array resolves or rejects.
const promise1 = new Promise((resolve) => {
setTimeout(resolve, 100, "First promise resolved.");
});
const promise2 = new Promise((resolve) => {
setTimeout(resolve, 200, "Second promise resolved.");
});
Promise.race([promise1, promise2])
.then((message) => {
console.log(message);
// Output: "First promise resolved."
})
.catch((error) => {
console.error(error);
});
Interviewers might ask to create a polyfill for this Promise.race method, This is one of the most asked interview questions.
Async/Await: Simplifying Promises
The async
and await
keywords provide a more concise and readable way to work with promises.
Basic Usage
An async
function always returns a promise. The await
keyword can only be used inside an async
function and pauses the execution of the function until the promise resolves.
async function fetchData() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
let data = await response.json();
console.log(data);
} catch (error) {
console.error("Error fetching data:", error);
}
}
fetchData();
Error Handling with Async/Await
Error handling with async/await
is done using try
and catch
blocks.
async function performAsyncTask() {
try {
let result = await someAsyncFunction();
console.log(result);
} catch (error) {
console.error("Error:", error);
}
}
performAsyncTask();
Differences Between Promises and Async/Await
Promises
- Execution: It is asynchronous in nature, which means it won’t wait till the promise is resolved/rejected. Once the promise is resolved then the “then” block will be executed.
- Syntax: Promises use the
then
andcatch
methods to handle asynchronous operations. - Error Handling: Errors are handled using the
catch
method.
Async/Await
- Execution: It is synchronous in nature, which means it will wait till the promise is resolved/rejected. The next code will be executed after the promise is resolved/rejected.
- Syntax: Uses the
async
keyword to declare an asynchronous function and theawait
keyword to wait for a promise to resolve. - Error Handling: Errors are handled using
try
andcatch
blocks, making the code more similar to synchronous code.
Conclusion
Promises and async/await
are powerful tools in JavaScript that simplify handling asynchronous operations. By understanding and utilizing these concepts, you can write more readable and maintainable code. Whether you’re fetching data from an API or performing complex asynchronous tasks, promises and async/await
make your life as a developer easier and your code more efficient.