如何解决页面请求接口大规模并发问题

324 阅读1分钟

在前端开发中,页面请求接口大规模并发可能会导致浏览器性能下降、服务器压力过大、接口响应超时等问题。以下是一些常见的解决方法:

1. 限制并发请求数量

通过自定义请求队列,控制同时发起的请求数量,避免过多请求同时发出。

示例代码

class RequestQueue {
    constructor(maxConcurrency) {
        this.maxConcurrency = maxConcurrency;
        this.currentConcurrency = 0;
        this.queue = [];
    }

    addRequest(request) {
        return new Promise((resolve, reject) => {
            const task = () => {
                this.currentConcurrency++;
                request()
                   .then((result) => {
                        resolve(result);
                    })
                   .catch((error) => {
                        reject(error);
                    })
                   .finally(() => {
                        this.currentConcurrency--;
                        if (this.queue.length > 0) {
                            const nextTask = this.queue.shift();
                            nextTask();
                        }
                    });
            };

            if (this.currentConcurrency < this.maxConcurrency) {
                task();
            } else {
                this.queue.push(task);
            }
        });
    }
}

// 使用示例
const queue = new RequestQueue(3); // 最大并发数为 3

function fetchData(id) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`Fetched data for id ${id}`);
            resolve(id);
        }, 1000);
    });
}

const requests = Array.from({ length: 10 }, (_, i) => () => fetchData(i));

requests.forEach((request) => {
    queue.addRequest(request).then((result) => {
        console.log(`Result: ${result}`);
    });
});

2. 合并请求

将多个相关的请求合并为一个请求,减少请求次数。

示例场景

例如,在电商页面中,需要获取多个商品的信息,可以将这些商品的 ID 一次性传递给后端接口,后端返回这些商品的详细信息。

// 假设我们有多个商品 ID
const productIds = [1, 2, 3, 4, 5];

// 合并请求
fetch(`/api/products?ids=${productIds.join(',')}`)
  .then((response) => response.json())
  .then((data) => {
        console.log(data);
    });

3. 节流与防抖

  • 节流(Throttle):在一定时间内,只执行一次请求。适用于需要频繁触发的请求,如滚动加载数据。
  • 防抖(Debounce):在一定时间内,只有最后一次触发的请求会被执行。适用于搜索框输入提示等场景。

节流示例代码

function throttle(func, delay) {
    let timer = null;
    return function() {
        if (!timer) {
            func.apply(this, arguments);
            timer = setTimeout(() => {
                timer = null;
            }, delay);
        }
    };
}

// 使用示例
function fetchDataOnScroll() {
    console.log('Fetching data on scroll');
    // 发起请求
}

window.addEventListener('scroll', throttle(fetchDataOnScroll, 500));

防抖示例代码

function debounce(func, delay) {
    let timer = null;
    return function() {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(this, arguments);
        }, delay);
    };
}

// 使用示例
function searchData(query) {
    console.log(`Searching for ${query}`);
    // 发起搜索请求
}

const input = document.getElementById('search-input');
input.addEventListener('input', debounce((e) => {
    searchData(e.target.value);
}, 300));

4. 缓存机制

对于一些不经常变化的数据,可以使用缓存机制,避免重复请求。

示例代码

const cache = {};

function fetchDataWithCache(url) {
    if (cache[url]) {
        return Promise.resolve(cache[url]);
    }

    return fetch(url)
      .then((response) => response.json())
      .then((data) => {
            cache[url] = data;
            return data;
        });
}

// 使用示例
fetchDataWithCache('/api/data')
  .then((data) => {
        console.log(data);
    });

5. 分批次请求

将大量请求分成多个批次,依次发送请求,避免一次性发送过多请求。

示例代码

const requests = Array.from({ length: 20 }, (_, i) => () => fetch(`/api/data/${i}`));
const batchSize = 5;

function sendRequestsInBatches() {
    const batch = requests.splice(0, batchSize);
    if (batch.length === 0) {
        return Promise.resolve();
    }

    const promises = batch.map((request) => request());
    return Promise.all(promises).then(() => sendRequestsInBatches());
}

sendRequestsInBatches().then(() => {
    console.log('All requests completed');
});