Fetch multiple API using Promises. You can fix 429 (too many requests)

Nay mình gặp một sự cố trong việc gọi API quá nhiều lần và nhận được thông báo lỗi 429 (too many requests). Mình đã tìm được cách fix lỗi đó cho vấn đề của mình, bằng cách thực hiện một số cách xử lý để hạn chế gọi lại nhiều lần đến server

Trong bài này mình sử dụng Promise để request api. Các bạn cũng chắc hiểu sơ qua về Promise và gặp nhiều trong các dự án rồi. 

Promise là một đối tượng trong JavaScript được sử dụng để thực hiện các tác vụ bất đồng bộ (asynchronous) và quản lý luồng điều khiển. Nó đại diện cho một giá trị chưa có sẵn tại thời điểm tạo ra promise, nhưng sẽ được trả về sau khi tác vụ hoàn thành hoặc thất bại.

Promise có hai trạng thái chính là "pending" (đang thực thi) và sau đó là "fulfilled" (hoàn thành) hoặc "rejected" (thất bại).

Promise cho phép bạn xử lý mã bất đồng bộ một cách dễ dàng hơn bằng cách sử dụng .then() để định nghĩa các hành động khi promise hoàn thành hoặc .catch() để xử lý lỗi khi promise thất bại. Điều này giúp tạo ra mã dễ đọc và quản lý hơn khi làm việc với các tác vụ bất đồng bộ như gọi API, tải tài liệu từ mạng, hoặc thực hiện các tác vụ đồng thời.

Ví dụ:

// 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);
  });

Các bạn có thể đọc thêm tại đây : Promise

Link Demo: https://hoanguyenit.com/Note-Dev/promises-all-api/

Giải quyết vấn đề sau:

+ Lấy dữ liệu categories, từ api, https://dummyjson.com/products/categories, đưa vào mảng arr_categories , để hồi ta sẽ for mảng arr_categories, hiện thị ra website

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

+ Cài đặt các API từng lại categories, VD: https://dummyjson.com/products/category/arr_categories[i] , dùng vòng lặp for mảng arr_categories

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);
 }

+ Sau khi đã có danh sách link api trong mảng link_categories, ta sẽ dùng map để lập fetch (url) , cài đặt giá trị timeout cho mỗi 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)
            );

+ Sau bước trên ta cần dùng promise để chạy 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);
              });

 Chú ý ở đây mình cần hiển thị dữ liệu đầu tiên ra website cho người dùng xem , hồi bạn xem full code bạn sẽ dễ hình dung hơn

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

Giờ ta chỉ cần lặp dữ liệu ra thôi

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);

Đoạn code trên ta cần cài đặt hàm  fetchDataAndUseIt(null, 0); để chèn giá thị tham số (name, id) để click vào category , mà gọi lại hàm đó

Okay, còn đầy là 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>

Hình Demo

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

Bài Viết Liên Quan

Messsage

Ủng hộ tôi bằng cách click vào quảng cáo. Để tôi có kinh phí tiếp tục phát triển Website!(Support me by clicking on the ad. Let me have the money to continue developing the Website!)