前端解决页面请求大规模并发问题
前端限制请求的并发数量,可以使用Promise.all来实现,但是Promise.all会等待所有的请求都完成后才会返回,这样会导致页面加载时间过长,所以我们可以使用Promise.race来限制请求的并发数量,当达到限制数量时,后面的请求会等待前面的请求完成后再执行。
具体实现如下:
- 定义一个请求队列,用来存储所有的请求。
- 定义一个并发数量,用来限制请求的并发数量。
- 定义一个函数,用来发送请求,并将请求添加到请求队列中。
- 定义一个函数,用来发送请求队列中的请求。
- 在发送请求的函数中,判断请求队列的长度是否小于并发数量,如果小于则发送请求,否则等待请求完成后再发送请求。
- 在请求完成后,从请求队列中移除该请求。
- 在请求完成后,判断请求队列是否还有请求,如果有则继续发送请求。
- 在请求完成后,判断请求队列是否为空,如果为空则表示所有请求都已完成。
- 在请求完成后,返回请求的结果。
- 在发送请求队列中的请求函数中,判断请求队列是否为空,如果为空则返回所有请求的结果。
class RequestQueue {
constructor(maxConcurrent) {
this.maxConcurrent = maxConcurrent; // 最大并发请求数
this.currentConcurrent = 0; // 当前并发请求数
this.queue = []; // 请求队列
this.processQueue(); // 开始处理队列
}
add(request) {
return new Promise((resolve, reject) => {
this.queue.push({ request, resolve, reject });
this.processQueue(); // 处理队列
});
}
processQueue() {
// 当前并发请求数小于最大并发请求数,并且队列中还有请求,则从队列中取出请求并执行
while (
this.currentConcurrent < this.maxConcurrent &&
this.queue.length > 0
) {
const { request, resolve, reject } = this.queue.shift();
this.currentConcurrent++;
request()
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
})
.finally(() => {
this.currentConcurrent--;
this.processQueue(); // 处理队列
});
}
}
}
// 示例请求函数
function fetchData(url) {
return fetch(url).then((response) => response.json());
}
// 使用请求队列
const requestQueue = new RequestQueue(5); // 设定最大并发请求数为5
const urls = [
"https://api.example.com/data1",
"https://api.example.com/data2",
"https://api.example.com/data3",
"https://api.example.com/data4",
"https://api.example.com/data5",
"https://api.example.com/data6",
"https://api.example.com/data7",
"https://api.example.com/data8",
"https://api.example.com/data9",
"https://api.example.com/data10",
]; // 假设我们有10个请求需要发送
const requests = urls.map((url) => () => fetchData(url)); // 将请求函数放入数组中
// // 将请求函数添加到请求队列中
// requests.forEach((request) => {
// requestQueue.add(request); // 添加请求到队列中
// });
// // 处理队列
// requestQueue.processQueue();
// 使用 Promise.all() 等待所有请求完成
// Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all(requests.map((request) => requestQueue.add(request)))
.then((responses) => {
console.log(responses);
console.log("所有请求完成", responses);
})
.catch((error) => {
console.error(error);
console.log("请求失败", error);
});
防抖、节流
- 防抖函数用于防止函数短时间内多次触发,只有当触发间隔大于指定时间间隔时才会执行函数。常用于输入框搜索、窗口大小调整等场景。
- 节流函数用于限制函数在一定时间间隔内只能执行一次,常用于滚动事件、窗口大小调整等场景。
- 防抖函数和节流函数都是通过 setTimeout 来实现的,区别在于防抖函数会清除计时器,而节流函数会设置一个计时器。
- 防抖函数会在最后一次触发后等待指定时间后执行函数,而节流函数会在第一次触发后立即执行函数。
- 防抖函数适用于只需要执行一次的场景,而节流函数适用于需要多次执行的场景。
// 防抖函数
function debounce(func, wait) {
let timeout;
return function () {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 节流函数
function throttle(func, wait) {
let timeout;
return function () {
const context = this;
const args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
}, wait);
}
};
}
// 防抖函数的使用
const handleSearch = debounce(() => {
// 处理搜索逻辑
})
// 输入框输入事件
// inputElement.addEventListener('input', handleSearch);
// 节流函数的使用
const handleScroll = throttle(() => {
// 处理滚动逻辑
console.log('handleScroll');
})
// 滚动事件
window.addEventListener('scroll', handleScroll);
// 注意:节流函数需要手动清除计时器,否则会导致无法再次触发。
// clearTimeout(timeout); // 清除计时器
// vue中防抖函数和节流函数的使用
// 在vue中,我们可以将防抖函数和节流函数封装成全局方法,然后在组件中使用。
// 在main.js中封装防抖函数和节流函数 (也可以直接在组件中import引入函数来使用)
Vue.prototype.$debounce = debounce;
Vue.prototype.$throttle = throttle;
// 在组件中使用
export default {
methods: {
handleSearch() {
// 处理搜索逻辑
}
},
mounted() {
// 输入框输入事件
this.$debounce(this.handleSearch, 300);
}
}