T
TanStack3y ago
broad-brown

how to make 100 sequential queries

Hi everyone, I hope you're all doing well. I find myself in a bit of a tricky situation and I'm unsure how to solve it. Currently, I'm using the useQueries hook to fetch dynamic data. The issue (which doesn't personally affect me, but rather the backend, it has to do a lot of work finding some heavy data in many DB's to be fair) is that the requests are being made in parallel, which I find great. However, when there are around 100 different requests, the backend crashes. As a result, I've been asked to modify the behavior to make the requests sequentially, either one after the other or in batches of 10 requests. If anyone could provide me with a hint or guidance on how to achieve this, I would greatly appreciate it.
import { useQueries } from '@tanstack/react-query';
import { getGeometriesByCatchmentId } from 'src/actions/globalFetchers';
export const useGeometriesList = (mapId: number, catchmentId: number[]) => {
// the list of catchemntId can be 100 or more ids

const queries = catchmentId.map((id) => ({
queryKey: ['geometries', id],
queryFn: () => getGeometriesByCatchmentId(mapId, id),
enabled: !!mapId || mapId !== 0,
refetchOnWindowFocus: false,
}))

const query = useQueries({
queries: queries,
})

return {
query,
}

};
import { useQueries } from '@tanstack/react-query';
import { getGeometriesByCatchmentId } from 'src/actions/globalFetchers';
export const useGeometriesList = (mapId: number, catchmentId: number[]) => {
// the list of catchemntId can be 100 or more ids

const queries = catchmentId.map((id) => ({
queryKey: ['geometries', id],
queryFn: () => getGeometriesByCatchmentId(mapId, id),
enabled: !!mapId || mapId !== 0,
refetchOnWindowFocus: false,
}))

const query = useQueries({
queries: queries,
})

return {
query,
}

};
3 Replies
noble-gold
noble-gold3y ago
If possible, I would try to do the batching inside the queryFn by: - replacing useQueries with a single useQuery - passing the catchmentId array down to the queryFn and let it loop and batch internally. That's a single query though with one result one loading state etc...
like-gold
like-gold3y ago
If you need individual loading states, you could control the parallelism at queryFn.
queryFn: limitParallelRequests(() =>
getGeometriesByCatchmentId(mapId, id)
),
queryFn: limitParallelRequests(() =>
getGeometriesByCatchmentId(mapId, id)
),
With limitParallelRequests defined as below.
let activeRequests = 0;
const pendingRequests: (() => void)[] = [];

async function acquire() {
activeRequests += 1;
if (activeRequests <= 10) {
return;
}
await new Promise<void>((resolve) => {
pendingRequests.push(resolve);
});
}

function release() {
activeRequests -= 1;
const resolve = pendingRequests.shift();
resolve?.();
}

type QueryFn<T> = () => Promise<T>;

function limitParallelRequests<T>(queryFn: QueryFn<T>): QueryFn<T> {
return async () => {
await acquire();
try {
return await queryFn();
} finally {
release();
}
};
}
let activeRequests = 0;
const pendingRequests: (() => void)[] = [];

async function acquire() {
activeRequests += 1;
if (activeRequests <= 10) {
return;
}
await new Promise<void>((resolve) => {
pendingRequests.push(resolve);
});
}

function release() {
activeRequests -= 1;
const resolve = pendingRequests.shift();
resolve?.();
}

type QueryFn<T> = () => Promise<T>;

function limitParallelRequests<T>(queryFn: QueryFn<T>): QueryFn<T> {
return async () => {
await acquire();
try {
return await queryFn();
} finally {
release();
}
};
}
broad-brown
broad-brownOP3y ago
chathurangakcd approach looks like what I want, thanks I’ll give it a shoot . Thanks @julien but I do need each query to be able to use the query methods later on. Hey @chathurangakcd thanks for that, it is exactly what i was looking for, it works perfectly.

Did you find this page helpful?