基于TanStack Query实现带延迟串行查询以规避Codeforces API限流
在TanStack Query中实现带延迟的串行API请求(适配Codeforces速率限制)
针对Codeforces API每2秒1次请求的限制,你可以通过以下几种便捷方式实现Q1、Q2、Q3的延迟串行执行,完全基于TanStack Query的原生能力:
方案一:利用enabled选项结合状态控制延迟
通过useQuery的enabled参数控制查询触发时机,配合定时器在指定延迟后开启后续请求:
import { useQuery, useState, useEffect } from '@tanstack/react-query'; // 假设这是你的API请求封装函数 const fetchUserData = (queryType) => { return fetch(`https://codeforces.com/api/${queryType}`).then(res => res.json()); }; function UserDataLoader() { // Q1:无延迟直接执行 const { isSuccess: q1Success, isError: q1Error } = useQuery({ queryKey: ['codeforces', 'q1'], queryFn: () => fetchUserData('user.info'), }); // Q2:Q1成功后延迟2秒执行 const [enableQ2, setEnableQ2] = useState(false); useEffect(() => { let timer; if (q1Success) { timer = setTimeout(() => setEnableQ2(true), 2000); } else if (q1Error) { setEnableQ2(false); // Q1失败则取消Q2 } return () => clearTimeout(timer); }, [q1Success, q1Error]); const { isSuccess: q2Success } = useQuery({ queryKey: ['codeforces', 'q2'], queryFn: () => fetchUserData('user.rating'), enabled: enableQ2, }); // Q3:Q1成功后延迟4秒执行(或基于Q2成功延迟2秒,按需调整) const [enableQ3, setEnableQ3] = useState(false); useEffect(() => { let timer; if (q1Success) { timer = setTimeout(() => setEnableQ3(true), 4000); } else if (q1Error) { setEnableQ3(false); } return () => clearTimeout(timer); }, [q1Success, q1Error]); useQuery({ queryKey: ['codeforces', 'q3'], queryFn: () => fetchUserData('user.status'), enabled: enableQ3, }); return ( <div> {q1Success && <p>Q1加载完成</p>} {q2Success && <p>Q2加载完成</p>} </div> ); }
优势:完全利用TanStack Query原生机制,每个查询独立维护缓存和状态,逻辑直观易调试。
方案二:使用prefetchQuery手动触发带延迟的查询
如果需要更主动的控制时机,可以用queryClient.prefetchQuery结合定时器,将数据预存入缓存,后续用useQuery读取:
import { useQuery, useQueryClient, useEffect } from '@tanstack/react-query'; import { fetchUserData } from './api'; function UserDataLoader() { const queryClient = useQueryClient(); useEffect(() => { // 先执行Q1,完成后触发延迟的Q2和Q3 const q1Promise = queryClient.prefetchQuery({ queryKey: ['codeforces', 'q1'], queryFn: () => fetchUserData('user.info'), }); q1Promise.then(() => { setTimeout(() => { queryClient.prefetchQuery({ queryKey: ['codeforces', 'q2'], queryFn: () => fetchUserData('user.rating'), }); }, 2000); setTimeout(() => { queryClient.prefetchQuery({ queryKey: ['codeforces', 'q3'], queryFn: () => fetchUserData('user.status'), }); }, 4000); }).catch(() => { // Q1失败时可取消后续请求 }); }, [queryClient]); // 从缓存中读取数据,自动处理加载/错误状态 const q1Data = useQuery({ queryKey: ['codeforces', 'q1'], queryFn: () => fetchUserData('user.info') }).data; const q2Data = useQuery({ queryKey: ['codeforces', 'q2'], queryFn: () => fetchUserData('user.rating') }).data; const q3Data = useQuery({ queryKey: ['codeforces', 'q3'], queryFn: () => fetchUserData('user.status') }).data; return <div>{/* 渲染数据 */}</div>; }
优势:适合需要手动触发查询的场景,预取数据不阻塞组件渲染。
方案三:封装自定义Hook实现复用逻辑
如果需要多次使用这种延迟串行查询,可以封装一个可复用的Hook:
import { useQuery, useState, useEffect } from '@tanstack/react-query'; function useDelayedQuery({ queryKey, queryFn, delay, trigger }) { const [enabled, setEnabled] = useState(false); useEffect(() => { let timer; if (trigger) { timer = setTimeout(() => setEnabled(true), delay); } else { setEnabled(false); } return () => clearTimeout(timer); }, [trigger, delay]); return useQuery({ queryKey, queryFn, enabled }); } // 使用示例 function UserDataLoader() { const q1Query = useQuery({ queryKey: ['codeforces', 'q1'], queryFn: () => fetchUserData('user.info'), }); const q2Query = useDelayedQuery({ queryKey: ['codeforces', 'q2'], queryFn: () => fetchUserData('user.rating'), delay: 2000, trigger: q1Query.isSuccess, }); const q3Query = useDelayedQuery({ queryKey: ['codeforces', 'q3'], queryFn: () => fetchUserData('user.status'), delay: 4000, trigger: q1Query.isSuccess, }); return <div>{/* 渲染数据 */}</div>; }
优势:代码更简洁,逻辑复用性强,减少重复代码。
注意事项
- 需处理请求失败的分支,比如Q1失败时是否取消后续请求,可结合
isError状态调整触发逻辑。 - 确保延迟时间严格符合API限制,避免因网络延迟导致实际请求间隔不足2秒。
- TanStack Query的缓存机制会自动生效,重复渲染时不会重复触发请求,除非缓存过期或手动调用
refetch。
内容的提问来源于stack exchange,提问作者Eshaan Aggarwal
相关产品推荐
相关产品推荐

