From 1eb42b451c6d06885061d7f9a5a7695485db6902 Mon Sep 17 00:00:00 2001 From: FAL-coffee Date: Mon, 9 Jun 2025 14:39:52 +0900 Subject: [PATCH] add parallelLimit option for SWRInfinite --- src/infinite/index.ts | 20 ++++++++++-- src/infinite/types.ts | 1 + test/use-swr-infinite.test.tsx | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/infinite/index.ts b/src/infinite/index.ts index 4292ea4486..d2a27c88ca 100644 --- a/src/infinite/index.ts +++ b/src/infinite/index.ts @@ -56,7 +56,8 @@ export const infinite = ((useSWRNext: SWRHook) => persistSize = false, revalidateFirstPage = true, revalidateOnMount = false, - parallel = false + parallel = false, + parallelLimit } = config const [, , , PRELOAD] = SWRGlobalState.get(defaultCache) as GlobalState @@ -215,9 +216,22 @@ export const infinite = ((useSWRNext: SWRHook) => } } - // flush all revalidateions in parallel + // flush all revalidations in parallel with optional limit if (parallel) { - await Promise.all(revalidators.map(r => r())) + if ( + parallelLimit && + parallelLimit > 0 && + revalidators.length > parallelLimit + ) { + // Process in batches with limited concurrency + for (let i = 0; i < revalidators.length; i += parallelLimit) { + const batch = revalidators.slice(i, i + parallelLimit) + await Promise.all(batch.map(r => r())) + } + } else { + // Traditional full parallel execution + await Promise.all(revalidators.map(r => r())) + } } // once we executed the data fetching based on the context, clear the context diff --git a/src/infinite/types.ts b/src/infinite/types.ts index 4237b6abae..1266ad2a70 100644 --- a/src/infinite/types.ts +++ b/src/infinite/types.ts @@ -39,6 +39,7 @@ export interface SWRInfiniteConfiguration< persistSize?: boolean revalidateFirstPage?: boolean parallel?: boolean + parallelLimit?: number fetcher?: Fn compare?: SWRInfiniteCompareFn } diff --git a/test/use-swr-infinite.test.tsx b/test/use-swr-infinite.test.tsx index cec8e7f462..d9afaa9974 100644 --- a/test/use-swr-infinite.test.tsx +++ b/test/use-swr-infinite.test.tsx @@ -1812,6 +1812,65 @@ describe('useSWRInfinite', () => { screen.getByText('data:apple, banana, pineapple,') }) + it('should support the parallelLimit option', async () => { + // mock api with delay tracking + const pageData = [ + 'apple', + 'banana', + 'pineapple', + 'orange', + 'grape', + 'cherry' + ] + const requestGroups: number[][] = [] + let currentBatch: number[] = [] + let batchStartTime = 0 + + const key = createKey() + function Page() { + const { data } = useSWRInfinite( + index => [key, index], + async ([_, index]) => { + const now = Date.now() + // Start a new batch if this is the first request or if more than 10ms has passed since the last batch + if (currentBatch.length === 0 || now - batchStartTime > 10) { + if (currentBatch.length > 0) { + requestGroups.push([...currentBatch]) + } + currentBatch = [index] + batchStartTime = now + } else { + currentBatch.push(index) + } + return createResponse(`${pageData[index]}, `, { delay: 50 }) + }, + { + initialSize: 6, + parallel: true, + parallelLimit: 2 + } + ) + + return
data:{data}
+ } + + renderWithConfig() + screen.getByText('data:') + + // Wait for all requests to complete + await act(() => sleep(300)) + screen.getByText('data:apple, banana, pineapple, orange, grape, cherry,') + + // Push the last batch if it's not empty + if (currentBatch.length > 0) { + requestGroups.push([...currentBatch]) + } + + // Verify that requests were made in batches of 2 + expect(requestGroups.length).toBeGreaterThanOrEqual(3) // Should have at least 3 batches for 6 items with limit 2 + expect(requestGroups.every(batch => batch.length <= 2)).toBeTruthy() // Each batch should have at most 2 items + }) + it('should make previousPageData null when the parallel option is enabled', async () => { // mock api const pageData = ['apple', 'banana', 'pineapple']