GraphQL如何使用useSubscription hook实现延迟读取数据
实现方案
分服务端和客户端两种实现路径,优先推荐服务端实现,可降低无效带宽消耗。
服务端实现(推荐)
核心思路是对MongoDB change stream的事件流做节流,每5秒仅推送一次时间窗口内的最新值,你可以借助RxJS的操作符快速实现:
import { fromEvent } from 'rxjs'; import { throttleTime } from 'rxjs/operators'; import { pubSub } from './你的pubSub实例路径'; const resolvers = { Subscription: { latestCollectionData: { subscribe: () => { // 初始化MongoDB change stream const changeStream = db.collection('目标集合名').watch([], { fullDocument: 'updateLookup' }); // 将change stream事件转为Observable流后做5秒节流 const throttledStream = fromEvent(changeStream, 'change').pipe( // trailing设为true保证取每个时间窗口的最后一条(即最新)数据 throttleTime(5000, undefined, { leading: false, trailing: true }) ); return pubSub.asyncIterator(throttledStream); } } } };
如果不想引入RxJS,也可以自己实现缓存逻辑:监听到change事件时仅更新缓存的最新值,每5秒检测一次缓存是否有更新,有更新则推送并清空缓存。
客户端实现(适合无法修改服务端的场景)
核心思路是先缓存所有推送来的最新值,每5秒仅更新一次视图状态,示例代码如下:
import { useSubscription, gql } from '@apollo/client'; import { useState, useEffect, useRef } from 'react'; // 你的订阅查询语句 const DATA_SUBSCRIPTION = gql` subscription SubscribeCollectionData { latestCollectionData { id value createTime } } `; function DataDisplay() { // 存储最新的推送数据,不直接触发视图更新 const latestDataRef = useRef(null); // 对外暴露的、用于视图渲染的数据 const [displayData, setDisplayData] = useState(null); const { data } = useSubscription(DATA_SUBSCRIPTION); // 收到订阅推送时更新缓存 useEffect(() => { if (data?.latestCollectionData) { latestDataRef.current = data.latestCollectionData; } }, [data]); // 每5秒读取一次缓存的最新值更新视图 useEffect(() => { const intervalTimer = setInterval(() => { if (latestDataRef.current) { setDisplayData(latestDataRef.current); } }, 5000); // 组件卸载时清除定时器 return () => clearInterval(intervalTimer); }, []); return <div>当前最新值:{displayData?.value ?? '无数据'}</div>; }
注意事项
- 高写入量场景下,还可以直接在服务端跳过change stream监听,改为每5秒直接查询MongoDB集合的最新数据推送,进一步降低服务端监听开销
- 两种方案均可以灵活调整时间窗口长度,把示例中的5000毫秒替换为你需要的数值即可
内容的提问来源于stack exchange,提问作者ATHER
相关产品推荐
相关产品推荐

