Angular开发中如何等待API请求完成后返回正确值
问题描述
抱歉如果该问题存在重复提问,我尝试自行排查解决但始终未找到正确的实现方法。
我正在搭建一个涵盖电影、剧集等内容的流媒体网站,需要实现返回指定剧集总集数的功能。
我的TypeScript组件代码如下:
export class MainCarouselComponent implements OnInit { constructor( private homeService: HomeService, private episodeService: EpisodesService ) {} MainCarousel?: IShow[]; EpisodesNum?: number; ngOnInit(): void { this.homeService.getHomeMainCarousel().subscribe((shows) => { this.MainCarousel = shows; this.MainCarousel.forEach( (show) => (show.runtime = this.calculateRuntime(show)) ); }); } calculateRuntime(_show: IShow): any { if (_show.runtime > 0) { var runtime = _show.runtime; var hours = runtime / 60; var rhours = Math.floor(hours); var minutes = (hours - rhours) * 60; var rminutes = Math.floor(minutes); return `${rhours} hr ${rminutes} min`; } else { return this.calculateEpisodes(_show.id); } } calculateEpisodes(_showId: number): number { //problem is here// let episodesNum; this.episodeService .getEpisodes(_showId) .subscribe((episodes) => (episodesNum = episodes.length)); return episodesNum; } }
当前我想要获取并返回剧集的总集数,但方法始终返回undefined。我清楚这是因为API请求属于异步操作,代码不会等待请求执行完成就直接返回变量,希望能得到该问题的正确解决方式,非常感谢。
问题根源
subscribe是异步非阻塞操作,发起HTTP请求后代码不会等待响应返回,会立刻执行return episodesNum,此时episodesNum尚未被赋值,因此返回undefined。本质错误是尝试在同步方法里直接返回异步请求的结果,逻辑本身无法成立。
解决方法
不要尝试从异步方法中同步返回值,调整处理流程:先拿到轮播位的所有内容数据,对无时长信息的剧集类内容并行拉取集数,等所有请求完成、展示字段处理完毕后再赋值触发视图渲染。修改后的可运行代码如下:
import { forkJoin } from 'rxjs'; export class MainCarouselComponent implements OnInit { constructor( private homeService: HomeService, private episodeService: EpisodesService ) {} MainCarousel?: IShow[]; ngOnInit(): void { this.homeService.getHomeMainCarousel().subscribe((shows) => { // 构造集数查询请求队列,电影类内容直接格式化时长 const episodeReqs = shows.map(show => { if (show.runtime > 0) { show.runtime = this.formatRuntime(show.runtime); return null; } return this.episodeService.getEpisodes(show.id); }); // 过滤掉空值,等待所有集数请求完成后统一处理 const validReqs = episodeReqs.filter(req => req !== null); forkJoin(validReqs).subscribe(episodeDataList => { let reqIndex = 0; shows.forEach(show => { if (show.runtime <= 0) { show.runtime = `共 ${episodeDataList[reqIndex].length} 集`; reqIndex++; } }); // 所有字段处理完成后再赋值,保证视图拿到完整数据 this.MainCarousel = shows; }) }); } // 抽离纯同步的时长格式化逻辑,不掺杂异步操作 private formatRuntime(runtime: number): string { const hours = Math.floor(runtime / 60); const minutes = Math.floor(runtime % 60); return `${hours} hr ${minutes} min`; } }
关键调整点
- 抽离纯同步的时长格式化方法,和异步逻辑完全解耦
- 用
forkJoin并行发起所有集数查询请求,减少整体加载等待时间 - 所有异步数据返回、展示字段处理完毕后,再给组件属性赋值,避免视图渲染时拿到不完整数据
- 删除原有错误的同步返回异步结果的方法,从根源避免时序问题
内容的提问来源于stack exchange,提问作者Emad Naeim
相关产品推荐
相关产品推荐

