React中如何顺序发起多个fetch请求获取OMDB API全部分页数据
问题原因
你的代码异常和await等待逻辑无关,核心是两个低级错误:
- 外层声明的
let movies没有设置初始值,默认是undefined - 循环内部用
const重新声明了同名movies变量,这个变量是循环块级作用域,仅在当前轮次生效,完全不会修改外层的movies,所以循环结束后外层变量还是undefined,自然会出现赋值为空、loading提前关闭的现象。
另外你的循环从page=1开始,会重复请求第一页数据,浪费请求额度。
正确实现
OMDB接口必须先拿到第一页返回的totalResults才能计算总页数,不需要单独发一次请求查总页数,第一页的返回数据可以直接复用,减少冗余请求。
方案1:顺序请求(推荐,避免触发免费版接口限流)
按页码顺序逐次请求,对接口压力小,不会触发频率限制:
const [loading, setLoading] = useState(true); const [data, setData] = useState([]); const fetchMovies = async () => { setLoading(true); const baseUrl = `http://www.omdbapi.com/?apikey=${API_KEY}&s=${searchResult}`; try { // 请求第一页数据,同时拿到总结果数 const firstResp = await fetch(baseUrl); const firstData = await firstResp.json(); // 初始化结果数组,直接存入第一页数据 let movies = firstData.Search ?? []; const totalPages = Math.ceil(Number(firstData.totalResults) / 10); // 从第二页开始循环请求,避免重复请求第一页 for (let page = 2; page <= totalPages; page++) { const pageResp = await fetch(`${baseUrl}&page=${page}`); const pageData = await pageResp.json(); // 直接修改外层的movies变量,不要重新声明同名变量 movies = [...movies, ...(pageData.Search ?? [])]; } // 所有请求执行完成后才会走到这里,统一更新数据 setData(movies); } catch (error) { console.log('请求失败:', error); } finally { // 无论成功失败都关闭loading,避免页面一直处于加载状态 setLoading(false); } }; useEffect(() => { fetchMovies(); }, [searchResult]); // 将搜索关键词加入依赖,关键词变化时自动重新拉取
注意:
for循环内部的await本身就会等待上一个请求resolve后才会执行下一轮,不需要额外做等待控制,只要变量作用域正确,一定会等所有请求完成才会执行循环后的逻辑。
方案2:并发请求(拉取速度快,适合总页数少的场景)
如果总页数不多,可以一次性构造所有分页请求,用Promise.all等待所有请求完成后合并结果,拉取速度更快:
const fetchMoviesParallel = async () => { setLoading(true); const baseUrl = `http://www.omdbapi.com/?apikey=${API_KEY}&s=${searchResult}`; try { const firstResp = await fetch(baseUrl); const firstData = await firstResp.json(); const totalPages = Math.ceil(Number(firstData.totalResults) / 10); // 构造剩余所有分页的请求Promise const pageRequests = []; for (let page = 2; page <= totalPages; page++) { pageRequests.push( fetch(`${baseUrl}&page=${page}`).then(res => res.json()) ); } // 等待所有分页请求完成 const allPageResult = await Promise.all(pageRequests); // 合并所有页的结果 const movies = allPageResult.reduce( (res, current) => [...res, ...(current.Search ?? [])], firstData.Search ?? [] ); setData(movies); } catch (error) { console.log('请求失败:', error); } finally { setLoading(false); } };
注意:OMDB免费版API有严格的请求频率限制,总页数超过5页时不建议用并发方案,很容易触发限流返回401/429错误。
疑问说明
- 不需要单独写逻辑先获取总页数再发请求:第一次请求第一页数据时已经能拿到
totalResults字段,直接复用这个返回值即可,多一次单独请求只会浪费接口额度。 - 不可能在首次请求内完成全量拉取:接口单次最多返回10条,必须分页请求,没有参数可以一次性拉回所有结果。
内容的提问来源于stack exchange,提问作者Olivier Moreau
相关产品推荐
相关产品推荐

