今天用到了tansktack-query进行请求,但是页面有个接口是获取数据的,需要定时更新,这个接口刷新的频率比较快,而且很多组件都会调用这个接口,但是需要对并发进行控制。
之前在面试字节的时候也是经常写这个题,所以印象也比较深。
于是我先搜索了一下,先来一个普通版本,我随便抄的一个
function getData() {
const limit = 5; // maximum concurrent requests
const dataUrls = ['https://example.com/data1.json',
'https://example.com/data2.json',
'https://example.com/data3.json',
'https://example.com/data4.json',
'https://example.com/data5.json',
'https://example.com/data6.json'];
let counter = 0;
const getDataPromise = dataUrl => {
return new Promise((resolve, reject) => {
fetch(dataUrl)
.then(response => {
counter--;
resolve(response);
})
.catch(error => {
counter--;
reject(error);
});
});
};
const getDataPromises = dataUrls.map(dataUrl => {
if (counter < limit) {
counter++;
return getDataPromise(dataUrl);
} else {
return new Promise(resolve => {
const interval = setInterval(() => {
if (counter < limit) {
counter++;
clearInterval(interval);
resolve(getDataPromise(dataUrl));
}
}, 100);
});
}
});
Promise.all(getDataPromises)
.then(responses => {
for (const response of responses) {
// handle each response here
}
})
.catch(error => {
console.error(error);
});
}
getData();
但是这个时候很巧AI给我提示了一个,我觉得非常有意思,而且对代码完全没有侵入
import { useQuery } from "@tanstack/react-query"
const MAX_CONCURRENT_REQUESTS = 3
let currentRequests = 0
type QueueItem = {
resolve: (value: any) => void
reject: (error: any) => void
request: () => Promise<any>
}
let requestQueue: QueueItem[] = []
const next = () => {
if (currentRequests < MAX_CONCURRENT_REQUESTS && requestQueue.length > 0) {
const nextItem = requestQueue.shift()
if (nextItem) {
currentRequests++
const { request, resolve, reject } = nextItem
request()
.then(resolve)
.catch(reject)
.finally(() => {
currentRequests--
next()
})
}
}
}
const queueRequest = (request: () => Promise<any>) => {
return new Promise((resolve, reject) => {
if (currentRequests < MAX_CONCURRENT_REQUESTS) {
currentRequests++
request()
.then(resolve)
.catch(reject)
.finally(() => {
currentRequests--
next()
})
} else {
requestQueue.push({ resolve, reject, request })
}
})
}
export const useChartData = (url: string, v: number, c: boolean) => {
useQuery({
queryKey: ['chartData', url, v, c],
queryFn: () => {
return queueRequest(() => fetch(url).then(res => res.json()))
},
enabled: !!url,
})
}
这个代码让我觉得有意思的点就是用promise的resolve和队列来进行请求的拦截,每次请求都把当前请求的promise放到队列,这样当队列满的时候,会一直进行等待,上一个完成之后就会自动next,在使用的时候只需要,对于任何方法都可以使用
queueRequest(() => fetch(url).then(res => res.json()))
实现上面的代码也很容易
const dataUrls = ['https://example.com/data1.json',
'https://example.com/data2.json',
'https://example.com/data3.json',
'https://example.com/data4.json',
'https://example.com/data5.json',
'https://example.com/data6.json'];
dataUrls.forEach(url => {
queueRequest(() => fetch(url).then(res => res.json())).then(data => {
console.log(data)
})
})
我看到这个代码的时候突然想到之前看的axios源码,里面拦截器也是通过队列存放拦截器的 resolve和reject,然后在外部触发拦截器的完成和失败