Promises and Error Handling Challenge
Scenario
You are developing an e-commerce dashboard that fetches data from various APIs, including a product catalog, user reviews, and a sales report. Each API call could potentially fail due to network issues, incorrect endpoints, or data inconsistencies. To build a stable and user-friendly application, you need to manage these scenarios with proper error handling.
Task
In this lab, you will build a small application that leverages asynchronous API calls using Promises. You will also implement error handling to ensure that the application can manage unexpected issues gracefully. This lab will test your ability to work with asynchronous programming concepts, as well as to manage errors effectively in a real-world scenario.
Objectives
By the end of this lab, you will be able to:
- Apply Promises to manage multiple asynchronous operations in JavaScript.
- Implement chained Promises to handle sequential data retrieval and manage dependencies between API calls.
- Utilize
.catch()
and.finally()
to handle errors and perform cleanup tasks in a Promise chain. - Design custom error classes to improve error identification and debugging.
- Implement a retry mechanism to manage failed asynchronous requests, enhancing application resilience.
- Analyze the benefits and challenges of using error handling strategies in complex asynchronous workflows.
Instructions
Part 1: Set Up Your Project
-
Create a New Project:
- Set up a new project folder, initialize it with
npm init -y
, and install any necessary dependencies, such as TypeScript.
- Set up a new project folder, initialize it with
-
Create an
apiSimulator.ts
file:- This file will contain functions that simulate API requests using Promises.
- Each function should return a Promise that resolves with mock data after a delay, or rejects with an error message.
Part 2: Implement API Simulation Functions
Simulate Asynchronous API Calls:
Create the following functions in apiSimulator.ts
, ensuring each returns a Promise:
fetchProductCatalog()
: Simulates fetching a list of products, each withid
,name
, andprice
.- Resolve the Promise with an array of mock products after a 1-second delay.
- Use
Math.random()
to sometimes reject the Promise with an error message, e.g.,"Failed to fetch product catalog"
.
Example Code for fetchProductCatalog()
export const fetchProductCatalog = (): Promise<{ id: number; name: string; price: number }[]> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.8) {
resolve([
{ id: 1, name: "Laptop", price: 1200 },
{ id: 2, name: "Headphones", price: 200 },
]);
} else {
reject("Failed to fetch product catalog");
}
}, 1000);
});
};
Continue with fetchProductReviews()
and fetchSalesReport()
functions similarly, adding realistic mock data and a chance for rejection.
-
fetchProductReviews(productId: number)
: Simulates fetching reviews for a product.- Resolve the Promise with an array of reviews after a 1.5-second delay.
- Reject the Promise randomly with an error message, e.g.,
"Failed to fetch reviews for product ID ${productId}"
.
-
fetchSalesReport()
: Simulates fetching a sales report withtotalSales
,unitsSold
, andaveragePrice
.- Resolve the Promise with a mock sales report after a 1-second delay.
- Reject randomly with an error message, e.g.,
"Failed to fetch sales report"
.
Part 3: Build the Main Application Logic
-
Create an
index.ts
file to contain the main logic of your application. -
Write a Function to Handle API Calls and Display Data:
- Use
fetchProductCatalog()
to fetch product details and display them. - For each product, fetch the reviews using
fetchProductReviews(productId)
. - After fetching products and reviews, retrieve the sales report using
fetchSalesReport()
.
- Use
-
Implement Error Handling Using Promises:
- Use
.catch()
to handle any errors fromfetchProductCatalog()
,fetchProductReviews()
, andfetchSalesReport()
. - Display error messages to the console if any of the calls fail.
- Use
.finally()
to log a message indicating that all API calls have been attempted.
- Use
Part 4: Custom Error Classes
- Create Custom Error Classes for different error scenarios:
NetworkError
for network-related issues.DataError
for data-related issues (e.g., missing fields in the API response).
- Update API Simulation Functions to use these custom error classes when rejecting Promises.
Part 5: Optional Challenge
-
Create a Retry Mechanism:
- Write a utility function
retryPromise
that accepts an async function, the number of retry attempts, and the delay between attempts.- Hint: Use
setTimeout
to delay the next attempt. - Hint: You will need to utilize recursion to implement this function. Not sure what recursion is, or don’t quite remember? This is an opportunity to practice your research abilities or review!
- Hint: Use
- Use this function to retry API calls that fail initially.
- Write a utility function
-
Implement
retryPromise
with API Calls to retry up to three times for each API call before giving up.
Critical Thinking Questions
- Why is it important to handle errors for each individual API call rather than just at the end of the promise chain?
- How does using custom error classes improve debugging and error identification?
- When might a retry mechanism be more effective than an immediate failure response?