Angular中等待异步函数执行完成后返回正确Menu值的方法
问题原因
你的代码存在典型的异步时序错误,执行顺序和预期完全不一致:
- 调用
getMenu()时,首先触发recupUser()发起HTTP GET请求,这是异步非阻塞操作,请求发出后代码不会等待响应返回,会直接继续往下执行 - 同步执行
return of(this.menu),这时候HTTP响应还没回来,subscribe回调还没触发,initMenu()根本没运行,this.menu要么是undefined要么是旧值 - 等HTTP响应返回后,才会进入回调给
this.User赋值、调用initMenu()给this.menu赋值,但这时候你之前返回的Observable早就已经把空值发给订阅方结束了,根本拿不到后续更新的菜单。
另外你的代码里还有一个逻辑bug:if (this.User.es = '04840')用了单等号赋值,不是相等判断,会导致判断条件永远为真,记得改成三等号===做全等校验。
修正方案
RxJS场景下不要在异步流程中间手动调用subscribe存全局变量再尝试同步返回值,应该用操作符把整个异步流程串成完整的流,等所有步骤执行完再吐出最终结果。
- 先把
initMenu改成接收User参数、直接返回Menu结构的纯函数,去掉对类全局变量的依赖:
private initMenu(user: User): Menu { // 修正原来的赋值bug,用全等判断 if (user.es === '04840') { return { leftMenu: [ {title: 'Title 1', id: '1', role: '/URL1'}, {title: 'Title 2', id: '2', role: '/URL2'}, ], rightMenu: [ {icon:'profile',tooltip:'Profile', id:'profile', role:'/'}, ] }; } return { leftMenu: [ {title: 'Title1', id: '1', role: '/URL1'}, ], rightMenu: [ {icon:'profile',tooltip:'Profile', id:'profile', role:'/'}, ] }; }
- 重写
getMenu方法,用RxJS操作符串起请求和菜单生成逻辑:
getMenu(): Observable<Menu> { return this.loginService.recupConnectedUser().pipe( // 如果其他逻辑需要用到当前登录用户信息,在这里赋值到类属性 tap(user => { this.User = user; }), // 拿到用户信息后同步生成对应菜单 map(user => this.initMenu(user)), // 如果其他逻辑需要缓存菜单实例,在这里赋值到类属性 tap(menu => { this.menu = menu; }) ); }
原来的recupUser方法如果没有其他地方单独调用可以直接删除,逻辑已经整合到流里;如果其他模块需要单独获取用户信息,就不要在方法内订阅,直接返回Observable本身即可:
recupUser(): Observable<User> { return this.loginService.recupConnectedUser().pipe( tap(user => this.User = user) ); }
调用注意事项
组件侧调用getMenu()时,必须在订阅回调里获取最终的菜单值,绝对不能尝试同步读取:
// 组件类中写法 this.menuService.getMenu().subscribe(menu => { // 这里拿到的才是请求完成、初始化完毕的正确菜单 this.renderedMenu = menu; });
更推荐的方式是在模板里用async管道处理订阅,自动管理取消订阅,避免内存泄漏:
// 组件类中定义流 menu$ = this.menuService.getMenu();
<!-- 模板中渲染 --> <ng-container *ngIf="menu$ | async as menu"> <!-- 菜单渲染逻辑写在这里,保证拿到的是完整有效的menu值 --> </ng-container>
核心异步原则
Angular中处理RxJS异步逻辑记住几个规则就能避免大部分时序问题:
subscribe是Observable流的终点,尽量只在最终调用方(组件)使用,或者交给async管道接管,不要在中间服务层随便订阅存临时变量- 所有依赖异步返回值的后续逻辑,必须放到
map/switchMap/tap等RxJS操作符的回调中执行,保证运行时机在异步响应返回之后 - 尽量把数据转换逻辑写成纯函数,输入确定则输出确定,减少对全局可变变量的依赖,从根源上避免“值还没赋值就被读取”的问题
内容的提问来源于stack exchange,提问作者OtiPeP
相关产品推荐
相关产品推荐

