DEV Community

Cover image for Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)
Hòa Nguyễn Coder
Hòa Nguyễn Coder

Posted on

Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)

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:

// 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);
  });
Enter fullscreen mode Exit fullscreen mode

You can read more here: Promise

Link Demo

Resolve the following problem:

let arr_categories = [];
const categories = fetch(
              `https://dummyjson.com/products/categories`
 );
 const categoriesResponse = await categories;
const categoriesData = await categoriesResponse.json();
Enter fullscreen mode Exit fullscreen mode
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);
 }
Enter fullscreen mode Exit fullscreen mode
  • 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
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)
            );
Enter fullscreen mode Exit fullscreen mode
  • After the above step, we need to use promise to run all fetch api,
// 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);
              });
Enter fullscreen mode Exit fullscreen mode

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.

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

Now we just need to iterate over the data

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);
Enter fullscreen mode Exit fullscreen mode

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

<!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>
Enter fullscreen mode Exit fullscreen mode

Demo:

Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)

The Article : Fetch Multiple API Using Promises. You Can Fix 429 (Too Many Requests)

Top comments (0)