React Native+ApolloJS:如何并行发送多个GraphQL请求(无await串行)
环境依赖
{ "@apollo/client": "^3.6.8", "apollo-link-logger": "^2.0.0", "apollo-link-token-refresh": "^0.4.0", "graphql": "^16.5.0", "graphql-tag": "^2.11.0", "react": "17.0.2", "react-native": "=0.68.0" }
问题描述
当前在React.FC中通过await串行调用Apollo Client请求,代码如下:
const loadData = async() => { await wait(800); await dataService1.getData1(); await dataService1.getData2(); await dataService1.getData3(); await dataService1.getData4(); await dataService1.getData5(); await dataService2.getData6(); await dataService3.getData7() setLoading(false); };
该方式可正常运行,但加载耗时超11秒,速度过慢。尝试移除await并行触发请求时,Apollo未发送全部请求,部分请求直接丢失。
每个请求都会触发中间件链中的拦截器(用于添加认证Bearer token),怀疑问题出在中间件链末端的httpLink,Apollo Client配置如下:
// Constructor of ApolloClientService this.httpLink = createHttpLink({ uri: `https://${this.api}/graphql`, }); // this.httpLink = new BatchHttpLink({ uri: `https://${this.api}/graphql`}); this.link = split(({ query }) => { const { kind, operation, }: { kind: string; operation?: string; } = getMainDefinition(query); return kind === 'OperationDefinition' && operation === 'subscription'; }, this.httpLink); this.defaultOptions = { watchQuery: { fetchPolicy: 'no-cache', errorPolicy: 'ignore', }, query: { fetchPolicy: 'no-cache', errorPolicy: 'all', }, mutate: { // errorPolicy: 'all', }, }; this.client = new ApolloClient<NormalizedCacheObject>({ link: ApolloLink.from([ onError(this.handleError), setContext(this.interceptRequest), apolloLogger, this.httpLink, ]), cache: new InMemoryCache(), defaultOptions: this.defaultOptions, connectToDevTools: false, queryDeduplication: false, });
限制条件:无法使用BatchLink(服务器不支持且暂无重构时间)、Apollo Hooks(useQuery/useMutation)或Apollo React组件方案,需要实现像REST API那样并行发送多个独立的GraphQL请求。
解决方案
1. 使用Promise.all实现并行请求
Apollo Client本身支持并行发送请求,直接移除await导致请求丢失的原因是未正确管理Promise生命周期。用Promise.all包裹所有请求Promise,可确保所有请求同时发起并等待全部完成:
const loadData = async() => { await wait(800); // 收集所有请求Promise,并行触发 const requestPromises = [ dataService1.getData1(), dataService1.getData2(), dataService1.getData3(), dataService1.getData4(), dataService1.getData5(), dataService2.getData6(), dataService3.getData7() ]; // 等待所有请求执行完成 await Promise.all(requestPromises); setLoading(false); };
2. 排查请求丢失的潜在原因
如果使用Promise.all后仍有请求丢失,需检查以下几点:
- 拦截器线程安全:确保
interceptRequest是无状态纯函数,每次调用返回独立的上下文对象,避免并行请求共享状态导致参数覆盖:const interceptRequest = async (operation, context) => { const token = await getCurrentToken(); // 从安全存储(如AsyncStorage)获取token return { ...context, headers: { ...context.headers, Authorization: token ? `Bearer ${token}` : '', }, }; }; - 错误处理链路:检查
onError拦截器是否中断了请求链路,确保即使单个请求报错,也不影响其他并行请求:const handleError = ({ graphQLErrors, networkError, operation, forward }) => { if (graphQLErrors) { graphQLErrors.forEach(({ message, locations, path }) => console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`) ); } if (networkError) console.log(`[Network error]: ${networkError}`); // 返回forward(operation)确保链路继续执行 return forward(operation); }; - React Native网络并发限制:React Native默认限制并发网络请求数(通常为6个),超过的请求会排队而非丢失。若请求数量较多,可分组并行执行:
const loadData = async() => { await wait(800); // 分批次并行请求 const batch1 = [dataService1.getData1(), dataService1.getData2(), dataService1.getData3()]; const batch2 = [dataService1.getData4(), dataService1.getData5(), dataService2.getData6()]; const batch3 = [dataService3.getData7()]; await Promise.all(batch1); await Promise.all(batch2); await Promise.all(batch3); setLoading(false); };
内容的提问来源于stack exchange,提问作者idRem7
相关产品推荐
相关产品推荐

