Angular服务层Observable返回值在组件subscribe外为undefined如何解决
问题根因
你遇到的变量undefined问题本质是异步执行时序问题:HTTP请求返回是异步的,subscribe里的赋值回调会在你同步写的this.isEverthingFailed = !this.investments && this.perfilFailed 这行代码执行之后才触发,所以你取值的时候两个变量还没被赋值。
解决方案
不需要额外引入Subject/BehaviorSubject,直接用RxJS提供的组合操作符forkJoin等待两个Observable都返回结果后再做计算即可,改写后的组件代码如下:
import { forkJoin } from 'rxjs'; public loadData() { // 组合两个请求,等所有请求都返回后统一处理逻辑 forkJoin([ this.homeGuardService.getPerfilNew$(), this.homeGuardService.getPositionConsolidated() ]) .pipe(takeUntil(this.unsubscribe)) .subscribe(([perfilRes, positionRes]) => { // 处理第一个请求返回值 this.perfilInvestidor = perfilRes; this.perfilFailed = this.perfilInvestidor.status ? true : false; console.log('perfil is failed --->', this.perfilFailed); // 处理第二个请求返回值 this.positionConsolidated = positionRes; this.investments = !!this.positionConsolidated; // 两个值都已赋值完成,再做计算 this.isEverthingFailed = !this.investments && this.perfilFailed; }, err => { // 统一处理请求错误逻辑,任意一个请求报错都会走到这里 console.error('请求异常', err); }) }
如果你确实需要两个请求独立处理、不需要等对方返回,也可以分别在两个subscribe的回调里增加判断,两个变量都赋值完成后再计算:
private checkAndCalcStatus() { // 两个变量都有值后再计算 if (this.perfilFailed !== undefined && this.investments !== undefined) { this.isEverthingFailed = !this.investments && this.perfilFailed; } } public loadData() { this.homeGuardService.getPerfilNew$.pipe( takeUntil(this.unsubscribe) ).subscribe(res => { this.perfilInvestidor = res; this.perfilFailed = this.perfilInvestidor.status ? true : false; this.checkAndCalcStatus(); }) this.homeGuardService.getPositionConsolidated() .pipe(takeUntil(this.unsubscribe)) .subscribe(res => { this.positionConsolidated = res; this.investments = !!this.positionConsolidated; this.checkAndCalcStatus(); }); }
常见疑问解答
1. 是否需要用Subject/BehaviorSubject?
不需要,当前场景是单组件内的异步结果合并,用RxJS组合操作符即可满足需求。如果你的perfilFailed、investments这两个状态需要在多组件间共享,才需要把状态存在服务层的BehaviorSubject里,多组件订阅共享状态。
2. 多个Observable会不会内存占用过高?
你当前的写法只要注意两点就不会有内存泄漏问题:
- 已经加了
takeUntil(this.unsubscribe)的请求,组件销毁时触发unsubscribe Subject就会自动取消订阅,不会留内存残留 - 你服务层用了
shareReplay(1),只要没有其他订阅者持有这个Observable的引用,GC会自动回收内存,不用担心占用过高
优化建议
- 服务层代码里的
map(res => res)是无效代码,没有做任何转换,可以直接删掉 - 两个请求调用的是同一个API地址
${environment.api.basePosicaoConsolidada}/consolidado,如果返回的结构是兼容的,完全可以只发一次请求,同时处理两个逻辑,减少不必要的HTTP请求开销 - 给
perfilFailed、investments变量加初始值(比如默认设为false),避免出现undefined的异常
内容的提问来源于stack exchange,提问作者Guilherme Lucas
相关产品推荐
相关产品推荐

