1. Why Promises and Async/Await Came into Existence
a. The Problem with Callbacks
Callback Hell: Nested callbacks made code difficult to read and maintain.
fetchData((data1) => {
processData(data1, (data2) => {
saveData(data2, (data3) => {
displayData(data3, (data4) => {
console.log("Final result:", data4);
});
});
});
});
Inversion of Control: Callbacks rely on external functions to execute them, leading to trust issues.
Error Handling: Handling errors in nested callbacks is cumbersome.
b. The Need for a Better Solution
Promises were introduced to handle asynchronous operations in a more structured and readable way.
Async/await was later introduced to make working with Promises even simpler and more intuitive.
2. Promises in JavaScript
a. What is a Promise?
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation.
It has three states:
Pending: Initial state, neither fulfilled nor rejected.
Fulfilled: The operation completed successfully.
Rejected: The operation failed.
b. Creating a Promise
A Promise is created using the Promise constructor, which takes an executor function with resolve and reject parameters.
Example:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Error fetching data!");
}
}, 1000);
});
c. Consuming a Promise
Use .then() for success and .catch() for failure.
Example:
promise
.then((result) => {
console.log(result); // "Data fetched successfully!"
})
.catch((error) => {
console.error(error); // "Error fetching data!"
});
d. Chaining Promises
Promises can be chained to handle multiple asynchronous operations sequentially.
Example:
fetchUserData()
.then((user) => {
console.log("User:", user);
return fetchUserPosts(user.id); // Return a new promise
})
.then((posts) => {
console.log("Posts:", posts);
return fetchPostComments(posts[0].id); // Return another promise
})
.then((comments) => {
console.log("Comments:", comments);
})
.catch((error) => {
console.error("Error:", error);
});
e. Promise Methods
Promise.resolve(): Creates a resolved promise.
const resolvedPromise = Promise.resolve("Resolved value");
resolvedPromise.then((value) => console.log(value)); // "Resolved value"
Promise.reject(): Creates a rejected promise.
const rejectedPromise = Promise.reject("Rejected value");
rejectedPromise.catch((error) => console.error(error)); // "Rejected value"
Promise.all(): Resolves when all promises in an array resolve.
Promise.all([p1, p2, p3])
.then((values) => console.log(values)) // [1, 2, 3]
.catch((error) => console.error(error));
Promise.race(): Resolves or rejects as soon as one promise in the array resolves or rejects.
Promise.race([p1, p2])
.then((result) => console.log(result)); // "Second"
3. Async/Await in JavaScript
a. What is Async/Await?
async/await is syntactic sugar for working with Promises, making asynchronous code look and behave like synchronous code.
async: Marks a function as asynchronous. It always returns a Promise.
await: Pauses the execution of an async function until the Promise is resolved.
b. How Async/Await Works Behind the Scenes
Async/await is built on top of Promises.
When you use await, the JavaScript engine pauses the function execution and waits for the Promise to resolve. Once resolved, it resumes execution.
Example:
async function fetchData() {
try {
const user = await fetchUserData(); // Pauses here until fetchUserData resolves
console.log("User:", user);
const posts = await fetchUserPosts(user.id); // Pauses here
console.log("Posts:", posts);
const comments = await fetchPostComments(posts[0].id); // Pauses here
console.log("Comments:", comments);
} catch (error) {
console.error("Error:", error);
}
}
fetchData();
c. Real-Life Example of Async/Await
Fetching data from an API:
async function fetchUserDetails() {
try {
const response = await fetch("https://api.example.com/user");
const user = await response.json();
console.log("User Details:", user);
} catch (error) {
console.error("Failed to fetch user details:", error);
}
}
fetchUserDetails();
4. Error Handling in Promises and Async/Await
a. Error Handling in Promises
Use .catch() to handle errors in Promises.
Example:
fetchUserData()
.then((user) => {
console.log("User:", user);
})
.catch((error) => {
console.error("Error:", error);
});
b. Error Handling in Async/Await
Use try/catch blocks to handle errors in async/await.
Example:
async function fetchData() {
try {
const user = await fetchUserData();
console.log("User:", user);
} catch (error) {
console.error("Error:", error);
}
}
5. Interview Questions
a. What is a Promise?
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation.
b. What are the states of a Promise?
A Promise has three states: Pending, Fulfilled, and Rejected.
c. What is the difference between Promise.all() and Promise.race()?
Promise.all() resolves when all promises in the array have resolved, while Promise.race() resolves or rejects as soon as one of the promises in the array resolves or rejects.
d. How does async/await work behind the scenes?
Async/await is built on top of Promises. The await keyword pauses the execution of an async function until the Promise is resolved.
e. How do you handle errors in async/await?
Errors in async/await are handled using try/catch blocks.
f. Can you convert a callback-based function to a Promise?
Yes, we can use the Promise constructor to wrap a callback-based function.
Example:
function fetchData() {
return new Promise((resolve, reject) => {
someCallbackFunction((error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
Comments
Post a Comment