如何基于Apollo React Native拆分大型GraphQL请求?
解决Apollo React Native请求体过大的拆分方案(WhoseApp通讯录查询场景)
针对你遇到的通讯录手机号批量查询请求被CDN拦截的问题,以下是两种拆分请求的实现方案,同时说明为什么useLazyQuery不是最优选择:
1. 顺序请求实现方案
顺序请求适合对并发数有严格限制的场景,避免触发CDN或后端的限流规则,实现逻辑如下:
步骤:
- 先将手机号数组按每组50个(可根据CDN限制调整)拆分,用简单的chunk工具函数处理:
const chunkArray = (arr, size) => { const chunks = []; for (let i = 0; i < arr.length; i += size) { chunks.push(arr.slice(i, i + size)); } return chunks; };
- 通过ApolloClient实例的
query方法,结合async/await循环处理每个分组:
import { useApolloClient } from '@apollo/client'; const checkContactStatuses = async (phoneNumbers) => { const client = useApolloClient(); const chunks = chunkArray(phoneNumbers, 50); const allResults = []; for (const chunk of chunks) { try { const { data } = await client.query({ query: GetPhoneNumberAccountStatus, variables: { phoneNumbers: chunk }, }); allResults.push(...data.getPhoneNumberAccountStatus); } catch (error) { // 处理单组请求失败,比如重试或记录错误 console.error(`Chunk request failed:`, error); // 可选:如果某组失败就终止流程,直接抛出错误 // throw error; } } return allResults; };
- 在组件中触发查询,比如用户注册流程中:
useEffect(() => { if (userIsRegistering && contactsLoaded) { checkContactStatuses(contactPhoneNumbers).then(results => { setRegisteredContacts(results); }); } }, [userIsRegistering, contactsLoaded]);
优势:
- 严格控制请求发送节奏,降低被限流的风险
- 错误处理更精细,可针对单组请求做重试逻辑
2. 并行请求实现方案
并行请求能大幅提升查询速度,适合后端和CDN允许一定并发数的场景,实现逻辑如下:
步骤:
- 同样先拆分手机号数组为分组
- 使用
Promise.all或Promise.allSettled并行发送请求,建议用并发限制工具(如p-limit)控制同时发送的请求数,避免触发限流:
import { useApolloClient } from '@apollo/client'; import pLimit from 'p-limit'; // 需要安装p-limit依赖 const checkContactStatusesParallel = async (phoneNumbers) => { const client = useApolloClient(); const chunks = chunkArray(phoneNumbers, 50); const limit = pLimit(5); // 限制同时发送5个请求,可根据实际调整 // 把每个分组请求包装成受限制的Promise const requests = chunks.map(chunk => limit(async () => { const { data } = await client.query({ query: GetPhoneNumberAccountStatus, variables: { phoneNumbers: chunk }, }); return data.getPhoneNumberAccountStatus; })); // 等待所有请求完成,合并结果 const results = await Promise.all(requests); return results.flat(); };
- 如果需要容忍部分请求失败,用
Promise.allSettled替代Promise.all,过滤出成功的响应:
const results = await Promise.allSettled(requests); const successfulResults = results .filter(res => res.status === 'fulfilled') .map(res => res.value) .flat();
优势:
- 相比顺序请求,查询耗时大幅缩短
- 利用并发提升用户体验,减少等待时间
为什么不推荐用useLazyQuery批量调用?
useLazyQuery是React Hook,必须遵循Hook规则(只能在组件顶层或自定义Hook中调用),批量循环调用会违反规则,导致组件状态混乱。此外,多个useLazyQuery实例的缓存和状态管理会变得复杂,难以统一处理所有请求的结果和错误,直接使用ApolloClient的query实例方法是更简洁、可控的方案。
内容的提问来源于stack exchange,提问作者OneHandClapping
相关产品推荐
相关产品推荐

