Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)
Tags: webdev, javascript, beginners, tutorial
Published: 2023-10-13 07:12:18

Now I have a problem in calling the API too many times and receiving error message  **429 (too many requests)** . I found a way to fix that error for my problem, by implementing some workarounds to limit multiple callbacks to the server.

In this article, I use Promise  to request api. You probably have a basic understanding of Promise and have encountered it in many projects. 

**Promise** is a JavaScript object used to perform **asynchronous** tasks and manage control flow. It represents a value that is not yet available at the time of promise creation , but will be returned once the task completes or fails.

**Promise** has two main states: "_pending_" and then "_fulfilled_" or "_rejected_".

**Promises** allow you to handle asynchronous code more easily by using **.then()** to define actions when the promise completes or **.catch()** to handle errors when the promise fails. This helps create more readable and manageable code when working with asynchronous tasks such as calling APIs, downloading documents from the network, or performing concurrent tasks.

**For example:**

```javascript
// Tạo một Promise để gọi API
const fetchData = () => {
  return new Promise((resolve, reject) => {
    // Giả sử đây là một tác vụ bất đồng bộ, ví dụ tải dữ liệu từ máy chủ
    setTimeout(() => {
      const data = { message: "Dữ liệu đã được tải thành công" };
      // Giả sử tác vụ thành công
      resolve(data);
      // Hoặc nếu tác vụ thất bại:
      // reject("Lỗi xảy ra khi tải dữ liệu");
    }, 2000);
  });
};

// Sử dụng Promise để gọi API và xử lý dữ liệu
fetchData()
  .then((data) => {
    console.log("Dữ liệu đã được tải xong:", data.message);
  })
  .catch((error) => {
    console.error("Lỗi khi tải dữ liệu:", error);
  });
```

You can read more here: [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)

[Link Demo](https://hoanguyenit.com/Note-Dev/promises-all-api/)

**Resolve the following problem:**

+ Get categories data, from the api,  **_https://dummyjson.com/products/categories_** , put it into the  **arr_categories** array , then we will put the **arr_categories** array , display it on the website

```javascript
let arr_categories = [];
const categories = fetch(
              `https://dummyjson.com/products/categories`
 );
 const categoriesResponse = await categories;
const categoriesData = await categoriesResponse.json();
```

+ Install APIs for each category, for example:  **_https://dummyjson.com/products/category/arr_categories[i]_** , use for loop for **arr_categories** array ,

```javascript
let link_categories = [];
for (let i = 0; i < categoriesData.length; i++) {
              arr_categories.push(categoriesData[i]);

              //push url to array link categories
              let link =
                "https://dummyjson.com/products/category/" + categoriesData[i];
              link_categories.push(link);
 }
```
 

+ After we have the list of api links in the **link_categories** array , we will use map to create fetch (url) , setting the timeout value for each fetch url

```javascript
const timeout = 5000; // Timeout 5 seconds (you can adjust this)
            const productPromises = link_categories.map((url) =>
              Promise.race([
                fetch(url).then((response) => response.json()),
                new Promise((_, reject) =>
                  setTimeout(
                    () => reject(new Error("Request Timeout")),
                    timeout
                  )
                ),
              ]).then((responseBody) => responseBody.products)
            );
```

+ After the above step, we need to use promise to run all fetch api,

```javascript
// we call array fetch categories
            Promise.all(productPromises)
              .then((products) => {
                data_temp = data_temp.concat(products);

                //after when , i have data, i will show data first to website
                if(dem==0){
                  append_html(data_temp[0]);
                }
                //end show

              })
              .catch((err) => {
                console.error("Failed to fetch data:", err);
              });
```

Note here that I need to display the first data on the website for users to see. When you see the full code, it will be easier to visualize.

```javascript
 //after when , i have data, i will show data first to website
if(dem==0){
    append_html(data_temp[0]);
}
```

Now we just need to iterate over the data

```javascript
async function fetchDataAndUseIt(name, id) {
        try {

          await call_api(dem); 

          if (id == 0 && dem == 0) {
            arr_categories.map((item, index) => {
              $(".add-categories").append(
                '<span class="text-white font-bold p-2 m-2 inline-block cursor-pointer bg-gray-400 rounded-md" onClick="fetchDataAndUseIt(\'' +
                  item +
                  "'," +
                  index +
                  ')">' +
                  item +
                  "</span>"
              );
              dem++;
            });
          }
          let mang = [];
          if (name == null) {
            mang = data_temp[0];
          } else {
            let index = arr_categories.findIndex((item) => item == name);
            console.log("index", index);
            mang = data_temp[index];
          }         
          append_html(mang);
          dem++;
        } catch (err) {
          console.error("Error:", err);
        }
      }
      let append_html = (mang) => {
        $(".add_product").html("");
        let html = "";
        for (let i = 0; i < mang.length; i++) {
          html += `<tr class="text-center" >
              <td  class="border border-slate-300">${mang[i].id}</td>
              <td  class="border border-slate-300">${mang[i].title}</td>
              <td  class="border border-slate-300 text-center"><img src='${mang[i].thumbnail}' width="100px" height="100px"></td>
            </tr>`;
        }
        $(".add_product").html(html);
      };

      fetchDataAndUseIt(null, 0);
```

In the above code we need to install the function   **fetchDataAndUseIt (null, 0)**; To insert the parameter value (n_ame, id_ ) click on the category , which calls the function again

Okay, it's Full Code

```javascript
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
      integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
    ></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
      let data_temp = [];
      let arr_categories = [];
      let call_api = async (dem) => {
        
        //if demo==0 , i will call first
        if (dem == 0) {
          try {
            const categories = fetch(
              `https://dummyjson.com/products/categories`
            );
            const categoriesResponse = await categories;
            const categoriesData = await categoriesResponse.json();

            let link_categories = [];
            for (let i = 0; i < categoriesData.length; i++) {
              arr_categories.push(categoriesData[i]);

              //push url to array link categories
              let link =
                "https://dummyjson.com/products/category/" + categoriesData[i];
              link_categories.push(link);
            }

            const timeout = 5000; // Timeout 5 seconds (you can adjust this)
            const productPromises = link_categories.map((url) =>
              Promise.race([
                fetch(url).then((response) => response.json()),
                new Promise((_, reject) =>
                  setTimeout(
                    () => reject(new Error("Request Timeout")),
                    timeout
                  )
                ),
              ]).then((responseBody) => responseBody.products)
            );

            // we call array fetch categories
            Promise.all(productPromises)
              .then((products) => {
                data_temp = data_temp.concat(products);

                //after when , i have data, i will show data first to website
                if(dem==0){
                  append_html(data_temp[0]);
                }
                //end show

              })
              .catch((err) => {
                console.error("Failed to fetch data:", err);
              });

            return data_temp; // return array products 
          } catch (err) {
            console.error("Failed to fetch data:", err);
            throw err; 
          }
        }
      };

      let dem = 0;
      async function fetchDataAndUseIt(name, id) {
        try {

          await call_api(dem); 

          if (id == 0 && dem == 0) {
            arr_categories.map((item, index) => {
              $(".add-categories").append(
                '<span class="text-white font-bold p-2 m-2 inline-block cursor-pointer bg-gray-400 rounded-md" onClick="fetchDataAndUseIt(\'' +
                  item +
                  "'," +
                  index +
                  ')">' +
                  item +
                  "</span>"
              );
              dem++;
            });
          }
          let mang = [];
          if (name == null) {
            mang = data_temp[0];
          } else {
            let index = arr_categories.findIndex((item) => item == name);
            console.log("index", index);
            mang = data_temp[index];
          }         
          append_html(mang);
          dem++;
        } catch (err) {
          console.error("Error:", err);
        }
      }
      let append_html = (mang) => {
        $(".add_product").html("");
        let html = "";
        for (let i = 0; i < mang.length; i++) {
          html += `<tr class="text-center" >
              <td  class="border border-slate-300">${mang[i].id}</td>
              <td  class="border border-slate-300">${mang[i].title}</td>
              <td  class="border border-slate-300 text-center"><img src='${mang[i].thumbnail}' width="100px" height="100px"></td>
            </tr>`;
        }
        $(".add_product").html(html);
      };

      fetchDataAndUseIt(null, 0);

    </script>
  </head>
  <body>
     <div class="mx-auto max-w-3xl">
      <h2 class="text-red-500 text-xl p-2 font-bold">List Categories</h2>
      <ul class="flex flex-row p-5 bg-gray-200 rounded-md">
        <li class="add-categories"></li>
      </ul>
      <div class="w-full p-2">
        <table class="w-full table">
          <caption>
            <h2 class="text-red-500 text-xl p-2 font-bold text-left">List Products</h2>
          </caption>
          <thead>
            <tr>
              <th class="border border-slate-300">ID</th>
              <th class="border border-slate-300">name</th>
              <th  class="border border-slate-300">thumbnail</th>
            </tr>
          </thead>
          <tbody class="add_product"></tbody>
        </table>
      </div>
      </div>class
  </body>
</html>
```
Demo:

![Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7090i18eyxcx5p0oftf.jpeg)

The Article : [Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)](https://hoanguyenit.com/fetch-multiple-api-using-promises-you-can-fix-429-too-many-requests.html)