Skip to content

feat: add parallelLimit option to useSWRInfinite #4133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/infinite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
persistSize = false,
revalidateFirstPage = true,
revalidateOnMount = false,
parallel = false
parallel = false,
parallelLimit
} = config
const [, , , PRELOAD] = SWRGlobalState.get(defaultCache) as GlobalState

Expand Down Expand Up @@ -215,9 +216,22 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
}
}

// flush all revalidateions in parallel
// flush all revalidations in parallel with optional limit
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo
revalidateions → revalidations

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
Expand Down
1 change: 1 addition & 0 deletions src/infinite/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface SWRInfiniteConfiguration<
persistSize?: boolean
revalidateFirstPage?: boolean
parallel?: boolean
parallelLimit?: number
fetcher?: Fn
compare?: SWRInfiniteCompareFn<Data>
}
Expand Down
59 changes: 59 additions & 0 deletions test/use-swr-infinite.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <div>data:{data}</div>
}

renderWithConfig(<Page />)
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']
Expand Down