Promise sử dụng kiểm soát requests đồng thời cao một cách hiệu quả

Nội dung bài viết

Video học lập trình mỗi ngày

Không đồng bộ với server?

Trong video "4 loại Promise khi nào và kịch bản nào nên sử dụng" thì có một câu hỏi được đưa ra của một bạn backend đó là có thể sử dụng promise để kiểm soát đồng thời request khi có số lượng lớn. Và muốn có một một video mô tả kỹ về điều này đồng thời triển khai code đó. Bài viết này chúng ta sẽ giải thích điều này thông qua một ví dụ thực tế về mô hình này.

Trong phát triển ứng dụng hiện này thì việc giao tiếp không đồng bộ với máy chủ là một yêu cầu khá phổ biến. Tuy nhiên, khi cần gửi nhiều yêu cầu cùng lúc, nếu không được kiểm soát có thể khiến máy chủ tải quá mức và ảnh hưởng đến hiệu suất ứng dụng. Bài viết này sẽ giới thiệu cách sử dụng Promise để kiểm soát số lượng yêu cầu đồng thời nhằm đảm bảo rằng các yêu cầu có thể được gửi và xử lý một cách có trật tự, nâng cao hiệu quả và tính ổn định của ứng dụng.

Promise sử dụng kiểm soát requests đồng thời cao

Vì sao phải hạn chế lượng đồng thời cao.

Trước hết, lập trình bất đồng bộ là giải quyết vấn đề thực thi các thao tác tốn thời gian trong chương trình. Ví dụ: giả sử bạn cần gửi nhiều yêu cầu đến máy chủ cùng lúc và đợi chúng trả về kết quả trước khi tiếp tục xử lý, nhưng bạn không muốn các yêu cầu này được thực hiện lần lượt theo thứ tự, vì điều này sẽ lãng phí thời gian. Lúc này, chúng ta có thể sử dụng Promise để kiểm soát số lượng request đồng thời nhằm nâng cao hiệu quả.

Nên xem: "4 loại Promise khi nào và kịch bản nào nên sử dụng"

Promise có thể được coi là một đối tượng đại diện cho kết quả của một hoạt động không đồng bộ. Nó có hai trạng thái quan trọng: đang chờ xử lý và đã hoàn thành hoặc bị từ chối. Khi chúng ta gửi yêu cầu, một đối tượng Promise tương ứng sẽ được tạo, đối tượng này có thể được sử dụng để theo dõi kết quả của yêu cầu. Chúng ta có thể gọi phương thức Promise và truyền vào hàm gọi lại để thực thi logic xử lý tương ứng sau khi yêu cầu được hoàn thành.

Promise hạn chế requests

Để kiểm soát tính đồng thời của các yêu cầu, chúng ta có thể tận dụng các tính năng không đồng bộ của Promise và JavaScript. Dưới đây là các bước để thực hiện, Source code và cách test ở đây

  1. Tạo hàng đợi yêu cầu để lưu trữ các yêu cầu đang chờ xử lý.
  2. Đặt giới hạn đồng thời, là số lượng yêu cầu tối đa có thể được xử lý đồng thời.
  3. Sử dụng bộ đếm để ghi lại số lượng yêu cầu hiện đang được xử lý, với giá trị ban đầu là 0.
  4. Duyệt qua hàng đợi yêu cầu và thực hiện các bước sau cho mỗi yêu cầu

Sau đây là code demo và cách test cho anh chị hiểu. Nếu anh chị cần có thể download mọi thứ ở đây...

  • Trường hợp 1, không sử dụng Promise

    const urls = [];
          for (let i = 1; i <= 21; i++) {
              setTimeout(function () {
                  fetch(`https://jsonplaceholder.typicode.com/todos/${i}`).then(rs => console.log(rs))
                      .catch(console.error())
              }, 1000)
    
          }
    

Trường hợp 2, sử dụng promise.all

Nếu chúng ta thử test case này thì như sau: Xem test. Kết quả sẽ áp lực rất lớn với server hiện tại. Như vậy không ổn. Sau khi fix sử dụng promise thì chúng ta lại test lại ở đây. Và kết quả như mong đợi. Source code và cách test ở đây

const concurrencyRequest = async (urls, maxNum) => {
            if (urls.length === 0) {
                // Nếu danh sách URLs trống, giải quyết ngay lập tức với mảng kết quả trống
                return Promise.resolve([]);
            }

            const results = []; // Mảng kết quả từ các yêu cầu
            let index = 0; // Chỉ số của URL đang được xử lý
            let count = 0; // Số lượng yêu cầu đã hoàn thành

            async function request() {
                if (index === urls.length) return; // Nếu đã xử lý tất cả các URL, thoát khỏi hàm

                const i = index; // Lưu chỉ số để sử dụng trong async function
                const url = urls[index++]; // Lấy URL và tăng chỉ số

                try {
                    // Thực hiện yêu cầu fetch và lưu kết quả vào mảng
                    results[i] = await fetch(url);
                } catch (err) {
                    // Nếu có lỗi, lưu lỗi vào mảng kết quả
                    results[i] = err;
                } finally {
                    // Tăng biến đếm và kiểm tra hoàn thành tất cả các yêu cầu
                    if (++count === urls.length) {
                        console.log('Hoàn thành tất cả yêu cầu');
                        resolve(results);
                    }

                    // Đặt thời gian chờ 1 giây và sau đó gọi lại hàm yêu cầu
                    setTimeout(request, 1000);
                }
            }

            const times = Math.min(maxNum, urls.length); // Số lần yêu cầu tối đa có thể được thực hiện đồng thời
            console.log(`:::001::`, times);

            // Bắt đầu thực hiện yêu cầu đồng thời
            Array.from({ length: times }, () => setTimeout(request, 1000));
        };

        const urls = [];
        for (let i = 1; i <= 21; i++) {
            urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
        }
        concurrencyRequest(urls, 3)

Có thể bạn đã bị missing