引言
在现代前端开发中,随着应用复杂度增加,接口请求管理成为性能优化的关键点。本文将深入探讨如何通过并发控制、数据缓存和请求合并等技术手段,构建高效、稳定的大规模请求解决方案。
并发控制
1.1 问题背景
当页面同时发起大量请求时,可能导致:
- 浏览器TCP连接数达到上限(Chrome同一域名下默认6个)
- 服务器压力剧增
- 低优先级请求阻塞关键请求
1.2 解决方案
使用请求队列实现控制并发
js
class RequestQ {
private _running;
private max;
private queue: ((value: unknown) => void)[];
constructor(max: number) {
this.max = max;
this.queue = [];
this._running = 0;
}
async add(fn: () => Promise<any>) {
if (this._running >= this.max) {
await new Promise((resolve) => this.queue.push(resolve));
}
this._running++;
try {
await fn();
} finally {
this._running--;
if (this.queue.length > 0) {
this.queue.shift()!();
}
}
}
}
这里主要使用Promise
来控制任务的并发,当当前运行的个数大于max时,会挂起一个Promise
等待它的resolve
,这里是实现并发控制的关键。
请求合并
js
class RequestMerge {
private pending;
constructor() {
this.pending = new Map();
}
async request(key: string, fn: Function) {
if (this.pending.get(key)) {
return this.pending.get(key);
}
const promise = fn().finally(() => {
this.pending.delete(key);
});
this.pending.set(key, promise);
return promise;
}
}
使用一个Map
用于储存正在请求的网络请求,当相同请求来的时候,先在Map
里去找是否有相同key的值,如果有直接返回,没有在去请求。
数据缓存
js
class CacheRequest {
private apiCache: Map<string, any>;
constructor() {
this.apiCache = new Map<string, any>();
setInterval(() => {
this.apiCache.forEach((value, key) => {
if (Date.now() > value.expireTime) {
this.apiCache.delete(key);
}
});
}, 60000);
}
async request(key: string, fn: () => Promise<any>) {
if (this.apiCache.has(key)) {
return this.apiCache.get(key);
}
const expireTime = Date.now() + 300000;
// eslint-disable-next-line no-useless-catch
try {
const result = await fn();
this.apiCache.set(key, { result, expireTime });
return result;
} catch (error) {
throw error;
}
}
}
类似与合并请求,不同的是这里是启动一个定时器,定期的去清理过期的key
。