如何在map函数中实现异步增量数据获取 满足剧集分步加载需求
问题根因
你之前的实现确实和async/await在map中的使用问题相关,核心有几个问题:
Array.map传入async函数时,返回的是promise数组,你没有对这些promise做等待处理,外层的try/catch无法捕获内部异步请求抛出的错误- 你在每个请求的前后都修改
isLoading,会导致第一个请求完成就把加载状态设为false,不符合「所有请求完成后才隐藏加载指示器」的需求 - 多个异步请求并发执行时,返回顺序不确定,最后渲染的剧集顺序可能和原
season.episodes的顺序不一致
原生React实现方案
如果你不需要引入额外库,可以根据业务需求选择对应的实现方式:
串行请求方案(保证剧集按原有顺序渲染,符合常规观看习惯)
const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [data, setData] = useState([]); const handleClick = async () => { // 点击后先初始化状态,打开全局加载 setIsLoading(true); setError(''); setData([]); try { // 用for of循环保证请求按顺序执行,逐个返回渲染 for (const episode of season.episodes) { const links = await publicQueryClientApi( `${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}` ); setData((prev) => [...prev, { episode, links }]); } } catch (error) { setData([]); setError(error.message); } finally { // 所有请求完成/出错,统一关闭加载状态 setIsLoading(false); } };
并发请求方案(请求速度更快,同时保证渲染顺序和原数组一致)
const handleClick = async () => { setIsLoading(true); setError(''); setData([]); try { // 并发发起所有请求 const requestPromises = season.episodes.map(async (episode) => { const links = await publicQueryClientApi( `${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}` ); return { episode, links }; }); // 按原数组顺序逐个处理返回结果,保证渲染顺序正确 for (const promise of requestPromises) { const res = await promise; setData(prev => [...prev, res]); } } catch (error) { setData([]); setError(error.message); } finally { setIsLoading(false); } };
RxJS适用性说明
RxJS完全适用于这个场景,它对这种异步流、逐个返回数据的场景处理能力很强,你可以用from把剧集数组转成可观察对象,再用mergeMap或者concatMap控制并发请求,用scan操作符累积返回的剧集数据,不需要手动维护state的更新逻辑,还能很方便的实现请求取消、重试、限流等额外能力,如果后续你的数据流逻辑变复杂,非常推荐引入RxJS来实现。
内容的提问来源于stack exchange,提问作者Yassine Bridi
相关产品推荐
相关产品推荐

